最短路
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条商店到赛场的路线。
输入保证至少存在1条商店到赛场的路线。
Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
Sample Input
2 11 2 33 31 2 52 3 53 1 20 0
Sample Output
32
Source
Recommend
Statistic | Submit | Discuss | Note
Floyd-Warshall
- 适用范围:无负权回路即可,边权可正可负,运行一次算法即可求得任意两点间最短路
- 时间复杂度:O(n^3)
原理
打开算法导论(英文版)第693页看看,我知道你不想看英文,所以看下面的个人理解和翻译。
假定结点集V为{1,2..n},对于 (i, j) 这条路,我们考虑它中途经过一些结点的所有情况(这些结点都取自集合{1,2,..k}),然后定义路径p为所有情况里的最短路径(即我们要找的答案路径)。那么关于k的p的关系有两种:
- k不在 最短路径p 里,即p里的点都是{1,2,..k-1}的点,则显然(i, j)经过{1,2,..k}的最短路 和 经过{1,2,..k-1}的最短路是一样的。
- 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)ij 前
dp[i][j]
存的是
d(k−1)ij,同理
d(k−1)ik 和
d(k−1)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的路径之和小,这就有点类似动态规划了。
实现
- dp数组对于不存在的边初始化为无穷大,但直接用INT_MAX的话在
dp[i][k] + dp[k][j]
的时候溢出,所以精确来说设置 为比全部路径的最大值大一点就行,如10条最大1000的边则设置为10*1000+1,但为写代码方便就用0x3f3f3f3f
(大概10亿)比较适合。 - 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; }