描述
灾后重建(rebuild)
B地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。
给出B地区的村庄数N,村庄编号从0到N-1,和所有M条公路的长度,公路是双向的。并给出第i个村庄重建完成的时间t[i],你可以认为是同时开始重建并在第t[i]天重建完成,并且在当天即可通车。若t[i]为0则说明地震未对此地区造成损坏,一开始就可以通车。之后有Q个询问(x, y, t),对于每个询问你要回答在第t天,从村庄x到村庄y的最短路径长度为多少。如果无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未重建完成 ,则需要返回-1。
输入格式
输入文件rebuild.in的第一行包含两个正整数N,M,表示了村庄的数目与公路的长度。
第二行包含N个非负整数t[0], t[1], …, t[N – 1],表示了每个村庄重建完成的时间,数据保证了t[0] ≤ t[1] ≤ … ≤ t[N – 1]。
接下来M行,每行3个非负整数i, j, w,w为不超过10000的正整数,表示了有一条连接村庄i与村庄j的道路,长度为w,保证i≠j,且对于任意一对村庄只会存在一条道路。
接下来一行也就是M+3行包含一个正整数Q,表示Q个询问。
接下来Q行,每行3个非负整数x, y, t,询问在第t天,从村庄x到村庄y的最短路径长度为多少,数据保证了t是不下降的。
输出格式
输出文件rebuild.out包含Q行,对每一个询问(x, y, t)输出对应的答案,即在第t天,从村庄x到村庄y的最短路径长度为多少。如果在第t天无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未修复完成,则输出-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
备注
【数据规模】
对于30%的数据,有N≤50;
对于30%的数据,有t[i] = 0,其中有20%的数据有t[i] = 0且N>50;
对于50%的数据,有Q≤100;
对于100%的数据,有N≤200,M≤N*(N-1)/2,Q≤50000,所有输入数据涉及整数均不超过100000。
代码
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 7 int n,m,point=0; 8 int T[505],g[505][505],note[505];//下标从0开始 9 10 void init_(){ 11 memset(g,-1,sizeof(g)); 12 scanf("%d%d",&n,&m); 13 14 for(int i=0;i<n;i++){ 15 scanf("%d",&T[i]); 16 if(T[i]==0) note[i]=1,point=i; 17 } 18 for(int i=1;i<=m;i++){ 19 int x,y,w; 20 scanf("%d%d%d",&x,&y,&w); 21 g[x][y]=w; 22 g[y][x]=w; 23 } 24 } 25 26 void floyd(int x){ 27 point=x+1,note[x]=1; 28 29 for(int i=0;i<n;i++){//更新到x的距离 30 for(int j=0;j<n;j++){ 31 if(note[i] && note[j] && g[i][j]!=-1 && g[j][x]!=-1){ 32 if(g[i][j]+g[j][x]<g[i][x] || g[i][x]==-1){ 33 g[x][i]=g[i][x]=g[i][j]+g[j][x]; 34 } 35 } 36 } 37 } 38 39 for(int i=0;i<n;i++){//更新以x为中继的点 40 for(int j=0;j<n;j++){ 41 if(note[i] && note[j] && g[i][x]!=-1 && g[x][j]!=-1){ 42 if(g[i][x]+g[x][j]<g[i][j] || g[i][j]==-1){ 43 g[i][j]=g[i][x]+g[x][j]; 44 } 45 } 46 } 47 } 48 } 49 50 51 void solve(){ 52 int x,y,t,ask; 53 scanf("%d",&ask); 54 55 if(point!=0){//对T[i]=0的点先进行最短路 56 for(int k=0;k<=point;k++) 57 for(int i=0;i<=point;i++) 58 for(int j=0;j<=point;j++) 59 if(g[i][k]!=-1 && g[k][j]!=-1) 60 if(g[i][k]+g[k][j]<g[i][j] || g[i][j]==-1) 61 g[i][j]=g[i][k]+g[k][j]; 62 } 63 64 while(ask--){ 65 scanf("%d%d%d",&x,&y,&t); 66 while(t>=T[point] && point<n) floyd(point); 67 if( note[x] && note[y] ) printf("%d\n",g[x][y]); 68 else puts("-1"); 69 } 70 } 71 72 int main(){ 73 // freopen("01.in","r",stdin); 74 init_(); 75 solve(); 76 return 0; 77 }不知道为什么洛谷过不了,TYVJ 和 CODEVS 都可以过
当然可以在线floyd,O(n^4),可以过4个点(我是不会说我之前就是这么做的)
转个题解:
数据规模比较小,所以用矩阵+离线floyd(在线spfa貌似要超时)
floyd算法中枚举的k是中转点,在这道题中就可以按时间顺序把点当作中转点,挨个儿加入图中,并且同时将‘时间恰当的询问’求出来(是指询问的时间<=t[k]的询问)
﹡注意题中所给的数据已经排好了序
最小瓶颈路:
题目大意:有一些村庄与它们之间的通路(即是图中的顶点和边),每条通路都有一个修复时间,要求何时所有村庄都可以连通。
其实就是一个无向带权图,要求最小瓶颈生成树(这棵树的边的最大值为这棵树的值)。可以用prim算法与kruskal算法。也可以用dijkstra算法。
为什么可以用dijkstra算法?首先,所有边权值非负。然后可以发现,当两个村庄相通时,它们的最短路(指的是瓶颈最短路)即为它们相通的最短时间,因为它们相通的条件是存在某几条修好的公路将它们连接,于是时间就取决于这几条公路中最迟建好的那条,于是把时间看成权,任意取一个顶点,使用DJ算法,求得他到其他所有公路的最短瓶颈路(最短时间内两村庄通车),因为是无向图,所以A到B的最短路即为B到A的最短路。于是源与其他所有顶点最短路的最大值即为所求。