题目背景
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。
这大概是我第一次独立A掉的提高+?
在这个特殊的日子里,我感觉我的生命在流逝 我来写博客纪念下。
不过这个题看起来挺水…
我太蒻了…
这道题求多源最短路,用floyd差不了了。
唉~不对…好像边会越来越多…
要不然加一次边跑一次spfa?
复杂度好像爆炸…
还是得用floyd…
那么只能一边加边一边跑了…
那么加一次边对那些点的最短路径有影响呢?
如果加的边val[u][v]比本来u和v的距离就大的话,好像没有什么影响…
因为要从u到v完全可以走之前的更短的路径而不是走直接相连的边…
如果比原来的距离大的话怎么办呢?
我们想想floyd算法,它是枚举每一个中间点,看看过这个点的路径能不能更新最短路…
也就是说,加了一条更短的边(u,v)以后,如果某两点间最短路径不经过边(u,v),那么它就不会受到影响。也就是说,只有经过点u和v的最短路径才可能受到影响。
那么我们就尝试用u和v作为中间点,尝试更新最短路!
问题好像迎刃而解了…
不存在的!
这样跑复杂度也会爆炸!
因为每一个刚刚建好的村庄可以和所有已经建好的村庄连线…而每个点都要求一遍最短路…
考虑优化,上面说到,只有经过并且经过v的最短路才会受到影响,所以我们可以用v作为中间点更新出u的到所有点的最短路,复杂度O(n),再用u作为中间点更新所有的最短路。
为什么是对的?
例如加边(u,v)后s–>t的最短路应更新为s–>…–>v–>u–>…–>t,如果用u作为中间点,我们就把最短路分成了两段,一段是s–>u,另一段是u–>t。u–t的最短路是不变的,上面的做法就是先更新s–>u的最短路(以v为中间点),这样就能求出s–>t的最短路了。
然后就过了!
(这个优化好像很弱)
代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
const int INF=1e9;
int val[201][201];
int dis[201][201];
int ed[201][201]={0};
int t[201];
int ans[50001];
int n,m;
void dijkstra(int k)
{
for(int i=0;i<n;i++)
{
if(ed[i][k]&&t[i]<=t[k]&&dis[i][k]>val[i][k]) //如果这条边已经建好了并且边长大于两点间本来的距离,就更新最短路
{
dis[i][k]=dis[k][i]=val[i][k];
for(int j=0;j<n;j++)
{
if(dis[j][i]+dis[i][k]<dis[j][k])
dis[j][k]=dis[k][j]=dis[j][i]+dis[i][k];
}
}
}
for(int i=0;i<n;i++) //以k为中间点的floyd
{
for(int j=0;j<n;j++)
{
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&t[i]);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
dis[i][j]=INF;
}
}
for(int i=1;i<=m;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
val[x][y]=val[y][x]=w;
ed[x][y]=ed[y][x]=1;
}
int q,u,v,w,s,p=0;
scanf("%d",&q);
for(int l=1;l<=q;l++)
{
scanf("%d%d%d",&u,&v,&s);
while(t[p]<=s&&p<n) //如果这个村庄在这一天已经修好了
{
dijkstra(p);
p++;
}
if(dis[u][v]>1e8) ans[l]=-1; //这个特判有些玄学...
else ans[l]=dis[u][v];
}
for(int i=1;i<=q;i++)
{
printf("%d\n",ans[i]);
}
return 0;
}