灾后重建(洛谷P1119,图论floyd)

                                                     P1119 灾后重建

题目背景

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

题目描述

给出BBB地区的村庄数NNN,村庄编号从000到N−1N-1N1,和所有MMM条公路的长度,公路是双向的。并给出第iii个村庄重建完成的时间tit_iti,你可以认为是同时开始重建并在第tit_iti天重建完成,并且在当天即可通车。若tit_iti000则说明地震未对此地区造成损坏,一开始就可以通车。之后有QQQ个询问(x,y,t)(x, y, t)(x,y,t),对于每个询问你要回答在第ttt天,从村庄xxx到村庄y的最短路径长度为多少。如果无法找到从xxx村庄到yyy村庄的路径,经过若干个已重建完成的村庄,或者村庄xxx或村庄yyy在第t天仍未重建完成 ,则需要返回−1-11。

输入格式

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

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

接下来MMM行,每行333个非负整数i,j,wi, j, wi,j,w,www为不超过100001000010000的正整数,表示了有一条连接村庄iii与村庄jjj的道路,长度为www,保证i≠ji≠ji=j,且对于任意一对村庄只会存在一条道路。

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

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

输出格式

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

输入输出样例

输入 #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
-1
5
4

说明/提示

对于30%30\%30%的数据,有N≤50

对于30%30\%30%的数据,有ti=0t_i= 0ti=0,其中有20%20\%20%的数据有ti=0N>50

对于50%50\%50%的数据,有Q≤100

对于100%100\%100%的数据,有N≤200M≤N×(N−1)/2,Q<=50000,所有输入数据涉及整数均不超过100000100000100000。

思路:非常好的floyd练习题,因为这道题我加深了对floyd本质的认识;

1 for(int k=1;k<=n;k++){
2     for(int i=1;i<=n;i++){
3         for(int j=1;j<=n;j++){
4             f[i][j]=f[i][k]+f[k][j];
5         }
6     }
7 }

floyd本质:最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,求任意两点之间的最短路程。用一句话概括就是:从i号顶点到j号顶点只经过前k号点的最短路程。

这也是此题的关键思路。

下面是代码(有详解)

 1 #include<cstdio>
 2 const int maxn=200+10,inf=0x3f3f3f3f;
 3 int a[maxn],w[maxn][maxn],f[maxn][maxn];
 4 int n;
 5 void Init(){
 6     for(int i=0;i<n;i++){
 7         for(int j=0;j<n;j++){
 8             f[i][j]=inf;
 9         }
10     }
11     for(int i=0;i<n;i++) f[i][i]=0;
12 }
13 void work(int k){
14     for(int i=0;i<n;i++){
15         for(int j=0;j<n;j++){
16             if(f[i][j]>f[i][k]+f[k][j]) f[i][j]=f[i][k]+f[k][j];
17         }
18     }
19 }
20 int main(){
21     int m;
22     scanf("%d%d",&n,&m);
23     for(int i=0;i<n;i++) scanf("%d",&a[i]);//读入每个村庄修建好时间(因为按顺序给出,所以不用先记录在排序)
24     Init();//初始化(普通的floyd)
25     for(int i=1;i<=m;i++){
26         int x,y,z;
27         scanf("%d%d%d",&x,&y,&z);
28         f[x][y]=f[y][x]=z;//初始化(普通的floyd)
29     }
30     int q;
31     scanf("%d",&q);
32     int last=0;
33     for(int i=1;i<=q;i++){
34         int x,y,t;
35         scanf("%d%d%d",&x,&y,&t);//读入每个询问
36         if(a[x]>t||a[y]>t){
37             printf("-1
");
38             continue;
39         }//如果x或y未修建好,直接输出-1
40         int j;
41         for(j=last;j<n;j++){
42             if(a[j]>t) break;
43             work(j);
44         }//寻找在t之前修好的且没有用过他来松弛的村庄来松弛,结合之前的初始化,开始时给f数组附上了一些值,不用担心这会影响结果,因为x,y在t或t之前被修好的
45         last=j;//所以j更新t以后的对答案无太大影响,且刚好他更新完了后边的,这就是floyd算法,所以之前的x或y是否修建好的判断一定要有,不然若j更新了,答案会错
46         if(f[x][y]==inf) printf("-1
");
47         else printf("%d
",f[x][y]);
48     }
49     return 0;
50 }
View Code
原文地址:https://www.cnblogs.com/HZOIDJ123/p/13423054.html