(dijkstra记录路径)find the longest of the shortest

Marica对Mirko很生气,因为他找到了一个新的女朋友,她想报仇。由于她不住在同一个城市,她开始为长途旅行做准备。我们知道每条路从一个城市到另一个城市需要多少分钟。

米尔科在车里无意中听到其中一条路正在维修,路被堵住了,但不知道具体是哪条路。无论哪条路是封闭的,从马里卡的城市到米尔科的都是可能的。

玛丽卡只走不堵塞的路,而且走最短的路。米尔科想知道她在最坏的情况下要多久才能到达他的城市,这样他就能确保他的女朋友离开这个城市足够长时间。编写一个程序,帮助Mirko找出Marica从没有堵塞的道路到他所在城市的最短路线所需要的几分钟内最长的时间。

输入

每一种情况都有两个数字在第一行,N和M,由一个单独的空间,城镇的数量,和城镇之间的道路的数量分开。1≤N≤1000,1≤≤N *(N - 1)/ 2。这些城市的编号从1到N, Mirko位于城市1,Marica位于城市N。

接下来的M行是三个数字A、B和V,用逗号隔开。1≤B≤N 1 V≤≤1000。这些数字意味着a和B城市之间有一条双向道路,而且在V分钟内是可以交叉的。

输出

在输出文件的第一行中以分钟为单位写入最大时间,Marica可能需要花几分钟才能到达Mirko。

样例输入

5 6

1 2 4

1 3 3

1 2 3

2 4 4

2 5 7

4 5 1

6 7
1 2 1

2 3 4

3 4 4

4 6 4

1 5 5

2 5 2

5 6 5

5 7

1 2 8

1 4 10

2 3 9

2 4 10

2 5 1

3 4 7

3 5 10

样例输出

11

13

27

题意:
最短指时间最短,坏一条边,此时的最短路的时间比其他坏一条边之后的最短路的时间长,输出这个时间

分析与解答

如果拆掉的边不在最初的最短路上,那么最短路的长度是不会变化的。
那么就只拆最短路上的边。
(上句话参考:http://blog.sina.com.cn/s/blog_8d84b9240101f827.html)
这样我们拆的范围就减小了,怎么算得上拆,就是让两个路的距离变成inf即可。由于拆的是最短路上的边,所以我们还要记录最短路的路径。拆完之后我们再调用dijkstra,此时我们不用再记录最短路的路径,而且调用完我们还得恢复拆的路,以便下一次拆其他不同的路。

我们看看怎么记录路径:

for(j=1;j<=m;j++)
if(!vis[j]&&dis[k]+map[k][j][j])
        {
            dis[j]=dis[k]+map[k][j];
            p[j]=k;//p数组记录路径 ,即j点前驱
        }   

我们找到了起点到终点k的最短路dis[k],现在以k为起点更新以其他点为终点的最短路dis[j],虽然有可能dis[j]再接下来的循环中会更短,但是没事,我们的路径的记录同样也会变的,所以我就可以在每次循环中就认定最短路中j的前驱就是k

代码参考:
https://blog.csdn.net/z8110/article/details/50061289

#include
#include
#include
#define N 0x3f3f3f3f
using namespace std;
int map[1100][1100],dis[1100],vis[1100],i,j,k,l,m,n,x,y,z,p[1100];
void dj(int v)//迪杰斯特拉算法 
{
    int i,j,k;
    memset(vis,0,sizeof(vis));
    memset(dis,N,sizeof(dis));
    dis[1]=0;//标记开始点已经被使用 
    for(i=1;i<=m;i++)
    {
        int Min=N;
        for(j=1;j<=m;j++)
        {
            if(!vis[j]&&dis[j]if(Min==N)//优化,如果没有找到较小的,直接结束 
        return ;
        vis[k]=1;
        for(j=1;j<=m;j++)
        if(!vis[j]&&dis[k]+map[k][j]map[k][j];
            if(v)
            p[j]=k;//p数组记录路径 ,即j点前驱
        }   
    }
}
int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        memset(p,0,sizeof(p));
        memset(map,N,sizeof(map));
//        for(i=1;i<=m;i++)
//        map[i][i]=0;
        for(i=0;iscanf("%d%d%d",&x,&y,&z);
            if(map[x][y]>z)
            map[x][y]=map[y][x]=z;
        }
        //int ans=dj(1);
        //printf("++%d\n",ans);
        dj(1);//以1为起点 
        int Max=dis[m];
        for(i=m;i!=1;i=p[i])//从m开始是因为p[j]=k是k连的j,一直到m 
        {
            z=map[i][p[i]];//把当前点和路径上的当前点的下一个点断开 
            map[i][p[i]]=map[p[i]][i]=N;
            dj(0);// 删边之后路径会改变,只记录未删时候的路径
            Max=max(Max,dis[m]); 
            map[i][p[i]]=map[p[i]][i]=z;//再连上 
        }
        printf("%d\n",Max);
    }
}

你可能感兴趣的:(acm练习(c++/c))