HDU 2544 最短路 // Floyd-warshall 算法 边权可正可负

本文详细介绍了使用Floyd-Warshall算法解决HDU2544最短路径问题的方法。该问题涉及寻找从商店到赛场的最短路径,适用于无负权回路的图。文章解释了算法原理,并提供了具体实现代码。

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

HDU 2544 最短路

 

Problem Description

在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

Input

输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。

Output

对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

Sample Input

2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

Sample Output

32

 

 

Floyd-Warshall

  • 适用范围:无负权回路即可,边权可正可负,运行一次算法即可求得任意两点间最短路
  • 时间复杂度:O(n^3)

原理

打开算法导论(英文版)第693页看看,我知道你不想看英文,所以看下面的个人理解和翻译。

假定结点集V为{1,2..n},对于 (i, j) 这条路,我们考虑它中途经过一些结点的所有情况(这些结点都取自集合{1,2,..k}),然后定义路径p为所有情况里的最短路径(即我们要找的答案路径)。那么关于k的p的关系有两种:

  1. k不在 最短路径p 里,即p里的点都是{1,2,..k-1}的点,则显然(i, j)经过{1,2,..k}的最短路 和 经过{1,2,..k-1}的最短路是一样的。
  2. k在 最短路径p 里,则p里的点都是{1,2,..k}的点,那我们可以把p分为(i,k)和(k,j),这两条分出来路径的只含{1,2,..k-1},因为p是最短路径,而k又在p里,所以(i,k)和(k,j)都是相对于(i,j)的最短路径 【此处算导没给证明,我们先假定自己认可这个结论】

算导图

根据上面的两种情况我们就可以得出递推式子

d(k)ij={wijifk=0min(d(k−1)ij,d(k−1)ik+d(k−1)kj)ifk≥1

注意k是集合大小,不是经过的点个数,k=0的时候是不经过任何中间点的情况,k>=1表示经过{1,2..k}这个集合里的点集。没看懂式子的话再看下那两种情况,看懂的话我们发现需要三维数组才能表示这种d(k)ij

,但在式子中我们的(k)其实只用在递推上,所以在上面代码中我们把k循环放在最外面就可以确保在计算d(k)ijdp[i][j]存的是d(k−1)ij,同理 d(k−1)ikd(k−1)ik

也是一样。这点也就类似背包的二维压成一维。

 

详细算法证明过程网址 :::https://blog.youkuaiyun.com/u012469987/article/details/51319574

 

void Floyd() //用来寻找任意两点间的最短路径
{
    for(int k=1;k<=n;k++)
    {
     for(int i=1;i<=n;i++)
     {
       for(int j=1;j<=n;j++)
       {
         dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
       }
     }
    }
}

意义在于::寻找i和j之间的最短距离,在从 i 通往 j 的道路中,可以分为以下两种通路:

1. i可以直接到 j

2.先从 i 到某一个点 k,再从点 k 到某一个点 j

我们要寻找的是最短的路径,所以就要判断,是从i直接到j的距离小,还是从i经过一个点k,再从点k经过一个点j的路径之和小,这就有点类似动态规划了。

 

实现

  1. dp数组对于不存在的边初始化为无穷大,但直接用INT_MAX的话在dp[i][k] + dp[k][j]的时候溢出,所以精确来说设置 为比全部路径的最大值大一点就行,如10条最大1000的边则设置为10*1000+1,但为写代码方便就用0x3f3f3f3f (大概10亿)比较适合。
  2. dp[i][i]初始化为0,即使就做题而言可能不会WA。

 

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<math.h>
using namespace std;
#define maxn 1001
#define INF 0x3f3f3f3f//注意这个INF的赋值 期初的错误在于这里 写成 #define INF 1000000 也能AC
int dp[maxn][maxn];
int n,m;
void Floyd() //用来寻找任意两点间的最短路径
{
    for(int k=1;k<=n;k++)
    {
     for(int i=1;i<=n;i++)
     {
       for(int j=1;j<=n;j++)
       {
         dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
       }
     }
    }
}

 int main()
{
   while(scanf("%d%d",&n,&m)!=EOF)
   {
       int a,b,c;
       memset(dp,0,sizeof(dp));
       if(n==0 ||m==0)
         break;
       for(int i=1;i<=n;i++)
       {
           for(int j=1;j<=n;j++)
           {
               if(i==j)
                dp[i][j]=0;
               else
                dp[i][j]=INF;
           }
       }
       for(int i=1;i<=m;i++)
       {
           scanf("%d%d%d",&a,&b,&c);
            dp[a][b]=c;
            dp[b][a]=c;
       }
       Floyd();
       cout<<dp[1][n]<<endl; //输出从商场到赛场的距离
   }
   return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值