描述
灾后重建(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。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std; int n,m,point=;
int T[],g[][],note[];//下标从0开始 void init_(){
memset(g,-,sizeof(g));
scanf("%d%d",&n,&m); for(int i=;i<n;i++){
scanf("%d",&T[i]);
if(T[i]==) note[i]=,point=i;
}
for(int i=;i<=m;i++){
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
g[x][y]=w;
g[y][x]=w;
}
} void floyd(int x){
point=x+,note[x]=; for(int i=;i<n;i++){//更新到x的距离
for(int j=;j<n;j++){
if(note[i] && note[j] && g[i][j]!=- && g[j][x]!=-){
if(g[i][j]+g[j][x]<g[i][x] || g[i][x]==-){
g[x][i]=g[i][x]=g[i][j]+g[j][x];
}
}
}
} for(int i=;i<n;i++){//更新以x为中继的点
for(int j=;j<n;j++){
if(note[i] && note[j] && g[i][x]!=- && g[x][j]!=-){
if(g[i][x]+g[x][j]<g[i][j] || g[i][j]==-){
g[i][j]=g[i][x]+g[x][j];
}
}
}
}
} void solve(){
int x,y,t,ask;
scanf("%d",&ask); if(point!=){//对T[i]=0的点先进行最短路
for(int k=;k<=point;k++)
for(int i=;i<=point;i++)
for(int j=;j<=point;j++)
if(g[i][k]!=- && g[k][j]!=-)
if(g[i][k]+g[k][j]<g[i][j] || g[i][j]==-)
g[i][j]=g[i][k]+g[k][j];
} while(ask--){
scanf("%d%d%d",&x,&y,&t);
while(t>=T[point] && point<n) floyd(point);
if( note[x] && note[y] ) printf("%d\n",g[x][y]);
else puts("-1");
}
} int main(){
// freopen("01.in","r",stdin);
init_();
solve();
return ;
}不知道为什么洛谷过不了,TYVJ 和 CODEVS 都可以过
当然可以在线floyd,O(n^4),可以过4个点(我是不会说我之前就是这么做的)
转个题解:
数据规模比较小,所以用矩阵+离线floyd(在线spfa貌似要超时)
floyd算法中枚举的k是中转点,在这道题中就可以按时间顺序把点当作中转点,挨个儿加入图中,并且同时将‘时间恰当的询问’求出来(是指询问的时间<=t[k]的询问)
﹡注意题中所给的数据已经排好了序
最小瓶颈路:
题目大意:有一些村庄与它们之间的通路(即是图中的顶点和边),每条通路都有一个修复时间,要求何时所有村庄都可以连通。
其实就是一个无向带权图,要求最小瓶颈生成树(这棵树的边的最大值为这棵树的值)。可以用prim算法与kruskal算法。也可以用dijkstra算法。
为什么可以用dijkstra算法?首先,所有边权值非负。然后可以发现,当两个村庄相通时,它们的最短路(指的是瓶颈最短路)即为它们相通的最短时间,因为它们相通的条件是存在某几条修好的公路将它们连接,于是时间就取决于这几条公路中最迟建好的那条,于是把时间看成权,任意取一个顶点,使用DJ算法,求得他到其他所有公路的最短瓶颈路(最短时间内两村庄通车),因为是无向图,所以A到B的最短路即为B到A的最短路。于是源与其他所有顶点最短路的最大值即为所求。