floyd——灾后重建(考察本质)
P1119 灾后重建 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这道题太好了,之前我只会打模板,对三重循环中的第一层循环概念并没有那么清晰,没有了解floyd的本质。这道题直接让我跪了,太牛了!!
开始分析:
这道题的关键点在于用floyd更新任意两点距离的时候,由于每个村落修路的时间不同,不可以直接将中转点设置为0~n-1的循环。这里需要深刻理解floyd的本质,其本质是从顶点i到顶点j只能经过前k个顶点的最短距离。既然是只能经过前k个点的最短距离,那么我们完全可以按照时间顺序,将该时间哪些村已经修完路的编号当作k,进行任意两点之间距离的更新。以前无脑打的模板中第一层循环k从0到n-1,而这道题抓住floyd的本质(用中转点更新最短距离),将k固定更新。不得不说一句,真是666666拉了。
而在q次询问之前先定义一个now的变量,用来表示该时间已经修完路的村落的编号(也就是中转点k的其中一个),当查询的时间比now对应的村落修路时间大的时候,说明此时可以将now当作中转点k进行一次floyd的更新,因为这道题村落的修路时间随编号的增加而增加,所以不用排序,直接now++,继续判断是否可以在该查询时间下将now当作k进行更新。
虽然有多次查询,但是now不用每次都初始化为0,因为修路时间递增,如果此时的now对应时间大于查询的时间,那么在之前查询时已经更新过以now作为中转点时任意两点的距离。每次now都初始化为0,耽误时间,没必要。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=250; 4 int dist[N][N],ti[N]; 5 int n,m,x,y,w,t; 6 const int INF=1e9; 7 8 void floyd(int k) 9 { 10 for(int i=0;i<n;i++) 11 { 12 for(int j=0;j<n;j++) 13 dist[i][j]=dist[j][i]=min(dist[i][j],dist[i][k]+dist[k][j]); 14 } 15 } 16 17 int main() 18 { 19 scanf("%d%d",&n,&m); 20 for(int i=0;i<n;i++)scanf("%d",&ti[i]); //输入每个村落修路的时间 21 22 for(int i=0;i<n;i++) //初始化任意两点的距离 23 { 24 for(int j=0;j<n;j++) 25 { 26 if(i==j)dist[i][j]=0; 27 else dist[i][j]=dist[j][i]=INF; 28 } 29 } 30 31 for(int i=1;i<=m;i++) //输入已知的两点距离 32 { 33 scanf("%d%d%d",&x,&y,&w); 34 dist[x][y]=dist[y][x]=w; 35 } 36 37 int q; 38 scanf("%d",&q); 39 40 int now=0; 41 //记录当前时间对应的编号(因为村落修路时间是递增,所以直接从第一个村落开始) 42 while(q--) 43 { 44 scanf("%d%d%d",&x,&y,&t); 45 while(ti[now]<=t&&now<n) 46 {//当now对应的村落修路时间小于等于t ,该村落就可以当floyd的中转点 47 floyd(now); 48 now++; //中转点逐渐增加 49 } 50 51 if(dist[x][y]==INF)printf("-1\n"); 52 else if(t<max(ti[x],ti[y]))printf("-1\n"); 53 else printf("%d\n",dist[x][y]); 54 } 55 56 return 0; 57 }