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

最短路

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 81645    Accepted Submission(s): 35359


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 11 2 33 31 2 52 3 53 1 20 0
 
Sample Output
 
  
32
 

Source
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   2066  1874  1217  2112  1142 
 

Statistic | Submit | Discuss | Note

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(k1)ij,d(k1)ik+d(k1)kj)ifk1

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

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

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


详细算法证明过程网址 :::https://blog.csdn.net/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

扫描二维码关注公众号,回复: 1108604 查看本文章

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


猜你喜欢

转载自blog.csdn.net/lmengi000/article/details/80149907
今日推荐