find the longest of the shortest HDU - 1595

本文介绍了一种解决特定图论问题的算法:如何找到在移除一条边后两指定点间最长最短路径的方法。通过预先计算最短路径并逐个分析其组成部分,实现了高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意: 

有一城市,这个城市有n个地点和m条连接他们的路,点的编号是从1到n,小X住在1,他想去n。

但是最近正在维修公路,也就是说这m条路有且只有一条是坏的,但是小X不知道是哪一条,一条很关键的路坏了路程就会增加很多,所以小X想知道从1到n *最*坏*情*况* 下的路程。你能帮助他吗?

Input

每组数据开头有一个N和M.1 ≤ N ≤ 1000, 1 ≤ M ≤ N*(N-1)/2. 代表点的数量和边的数量,小X在1,他想去n. 然后有M行,每行有三个数 X , Y  , Z.  1 ≤ X,Y ≤ N, 1 ≤ Z ≤ 1000.代表X和Y之间有一条双向的公路,它的长度是Z.

 

Output

输出最坏情况下的路程。

Sample Input

5 6
1 2 4
1 3 3
2 3 1
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

Sample Output

11
13
27

思路:

       题意只要求删除一条边,所以最容易想到的方法就是以次删除M条边的一条,然后求一次Dijkstra保留最大的距离。但是这种简单的暴力求解是一定会超时的。题意让我们找的是去掉一条边后的最短路的最大值,假设在不删除边的情况下能找到一条1到n最短路s1,而在删除一条边的情况下我们找到一条最短路s2。

      s1唯一或不唯一对下面的的分析没有影响,假设s1唯一。

      当删除的边不在s1的路径上,那么s2==s1,所以如果不是删除s1上的路径,我们所求的s2是没有意义的。所以应该删除s1上的边,做法应该是在不删除边的情况下求一次最短路并把路径保留下来,然后依次删除s1上的路径保留1到n的最大值就行了。

代码:

#define N    1010
#define mod  1000000007
#define PI   3.1415926
#define inf  0x3f3f3f3f
#define poor 1000

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<math.h>

#include<stdio.h>
#include<string.h>

#include<iostream>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,m,a,b,c,flag;
int k[N][N],dis[N];
bool book[N];
int pre[N*100];
//pre[v]=u  在最短路中存在u->v这条边(1~>v是通过1~>u与u->v更新而来的)
int Dijkstra()
{
    memset(book,0,sizeof(book));
    for(int i=1; i<=n; i++)
        dis[i]=k[1][i];
    book[1]=1;
    dis[1]=0;
    for(int i=0; i<n; i++)
    {
        int minn=inf,u;
        for(int j=1; j<=n; j++)
            if(!book[j]&&dis[j]<minn)
        {
            minn=dis[j];
            u=j;
        }
        if(minn==inf)break;
        book[u]=1;
        for(int v=1; v<=n; v++)
        {
            if(k[u][v]<inf)
            {
                if(dis[v]>k[u][v]+minn&&!book[v])
                {
                    dis[v]=k[u][v]+minn;
                    if(!flag)pre[v]=u;//通过u->v这条边可以更新而来的
                }
            }
        }
    }
    return dis[n];
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        flag=0;
        fill(pre,pre+n,1);
        //假设开始时所有边都与1相连,就是1->i(如果1->i没有直接相连dis[i]=inf)
        for(int i=1; i<=n; i++)
        {
            k[i][i]=0;
            for(int j=i+1; j<=n; j++)
                k[i][j]=k[j][i]=inf;
        }
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(k[a][b]>c)
                k[a][b]=k[b][a]=c;
        }
        int maxx=Dijkstra();
        flag=1;
        int x=n;
        while(x!=1)//回溯1~>n所经过的路径,同时删除
        {
            //printf("%d<-%d\n",x,pre[x]);
            int c=k[x][pre[x]];
            k[x][pre[x]]=k[pre[x]][x]=inf;//删除这条边就是假设这条边为inf
            maxx=max(maxx,Dijkstra());//保留最坏的情况
            k[x][pre[x]]=k[pre[x]][x]=c;//还原
            x=pre[x];
        }
        printf("%d\n",maxx);
    }
    return 0;
}





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值