P1119 灾后重建(29-29)

141 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 29 天,点击查看活动详情

P1119 灾后重建 洛谷

灾后重建

题目背景

B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。

题目描述

给出 B 地区的村庄数 NN,村庄编号从 00N1N-1,和所有 MM 条公路的长度,公路是双向的。并给出第 ii 个村庄重建完成的时间 tit_i,你可以认为是同时开始重建并在第 tit_i 天重建完成,并且在当天即可通车。若 tit_i00 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 QQ 个询问 (x,y,t)(x,y,t),对于每个询问你要回答在第 tt 天,从村庄 xx 到村庄 yy 的最短路径长度为多少。如果无法找到从 xx 村庄到 yy 村庄的路径,经过若干个已重建完成的村庄,或者村庄 xx 或村庄 yy 在第 tt 天仍未重建完成,则需要返回 -1

输入格式

第一行包含两个正整数N,MN,M,表示了村庄的数目与公路的数量。

第二行包含NN个非负整数t0,t1,,tN1t_0, t_1,…, t_{N-1},表示了每个村庄重建完成的时间,数据保证了t0t1tN1t_0 ≤ t_1 ≤ … ≤ t_{N-1}

接下来MM行,每行33个非负整数i,j,wi, j, www为不超过1000010000的正整数,表示了有一条连接村庄ii与村庄jj的道路,长度为ww,保证iji≠j,且对于任意一对村庄只会存在一条道路。

接下来一行也就是M+3M+3行包含一个正整数QQ,表示QQ个询问。

接下来QQ行,每行33个非负整数x,y,tx, y, t,询问在第tt天,从村庄xx到村庄yy的最短路径长度为多少,数据保证了tt是不下降的。

输出格式

QQ行,对每一个询问(x,y,t)(x, y, t)输出对应的答案,即在第tt天,从村庄xx到村庄yy的最短路径长度为多少。如果在第t天无法找到从xx村庄到yy村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄yy在第tt天仍未修复完成,则输出1-1

样例输入

4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4

样例输出

-1
-1
5
4

题目分析

本题对理解动态规划下的flody很有帮助。

首先对题目进行大致分析,有初始编号为 0n10\sim n-1nn 个村庄,两两之间具有一个双向的道路,但相邻两村庄的道路需要在 min(ta,tb)min(t_a,t_b) 天才可以启用。给出 qq 个询问,每个询问为第 tt 天从村庄 aabb 的距离。题目保证村庄 tt 升序且询问的 tt 升序。

由上一题的分析,定义 f[i][j][k]f[i][j][k] 为起点为 ii 终点为 jj 途径 0k0\sim k 中某些点的最短路径。

对于 kk 是采用从小到大遍历,即第 kk 层的遍历需要第 k1k-1 层为基础,这正与此题的分析不谋而合,假设此时时间为 tt,将其作为第 kk 层,恢复时间小于 tt 的为前 k1k - 1 层,我们依次遍历前面的层数便可得到当前时间的答案。

Accept代码 O(n3+q)O(n^3+q)

#include <bits/stdc++.h>

using namespace std;

const int N = 210, INF = 1e9;

int n, m, q;
int st[N], g[N][N];

void flody(int k)
{
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            g[i][j] = g[j][i] = min(g[i][j], g[i][k] + g[k][j]);
}

int main()
{
    cin >> n >> m;
    
    for (int i = 0; i < n; i ++) cin >> st[i];
    
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            if (i != j) g[i][j] = INF;

    for (int i = 0; i < m; i ++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        
        g[a][b] = min(g[a][b], c);
        g[b][a] = min(g[b][a], c);
    }
    
    cin >> q;
    int now = 0;
    for (int i = 0; i < q; i ++)
    {
        int a, b, t;
        cin >> a >> b >> t;
        
        while (st[now] <= t && now < n)
        {
            flody(now);
            now ++;
        }
        
        if (st[a] > t || st[b] > t || g[a][b] == INF) cout << -1 << "\n";
        else cout << g[a][b] << "\n";
    }
    return 0;
}