洛谷 P1119 灾后重建

来源:互联网 发布:淘宝分销商好做吗 编辑:程序博客网 时间:2024/06/07 22:47

题目背景

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;}

原创粉丝点击