仅仅有五行的Floyd最短路算法

暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,例如以下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道随意两个城市之前的最短路程。

上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。

我们如今须要求随意两个城市之间的最短路程,也就是求随意两个点之间的最短路径。这个问题这也被称为“多源最短路径”问题。

如今须要一个数据结构来存储图的信息,我们仍然能够用一个4*4的矩阵(二维数组e)来存储。比方1号城市到2号城市的路程为2,则设e[1][2]的值为2。2号城市无法到达4号城市,则设置e[2][4]的值为∞。

另外此处约定一个城市自己是到自己的也是0。比如e[1][1]为0,详细例如以下。

如今回到问题:怎样求随意两点之间最短路径呢?通过之前的学习我们知道通过深度或广度优先搜索能够求出两点之间的最短路径。所以进行n2遍深度或广度优先搜索,即对每两个点都进行一次深度或广度优先搜索,便能够求得随意两点之间的最短路径。

但是还有没有别的方法呢?

我们来想一想,依据我们以往的经验。假设要让随意两点(比如从顶点a点到顶点b)之间的路程变短。仅仅能引入第三个点(顶点k)。并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程。那么这个中转的顶点k是1~n中的哪个点呢?甚至有时候不仅仅通过一个点,而是经过两个点或者很多其它点中转会更短。即a->k1->k2b->或者a->k1->k2…->k->i…->b。

比方上图中从4号城市到3号城市(4->3)的路程e[4][3]原本是12。

假设仅仅通过1号城市中转(4->1->3),路程将缩短为11(e[4][1]+e[1][3]=5+6=11)。事实上1号城市到3号城市也能够通过2号城市中转。使得1号到3号城市的路程缩短为5(e[1][2]+e[2][3]=2+3=5)。所以假设同一时候经过1号和2号两个城市中转的话。从4号城市到3号城市的路程会进一步缩短为10。通过这个的样例,我们发现每一个顶点都有可能使得另外两个顶点之间的路程变短。好,以下我们将这个问题一般化。

当随意两点之间不同意经过第三个点时。这些城市之间最短路程就是初始路程,例如以下。

如如今仅仅同意经过1号顶点,求随意两点之间的最短路程,应该怎样求呢?仅仅需推断e[i][1]+e[1][j]是否比e[i][j]要小就可以。

e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。

当中i是1~n循环,j也是1~n循环,代码实现例如以下。

  1. for(i=1;i<=n;i++)
  2. {
  3. for(j=1;j<=n;j++)
  4. {
  5. if ( e[i][j] > e[i][1]+e[1][j] )
  6. e[i][j] = e[i][1]+e[1][j];
  7. }
  8. }

在仅仅同意经过1号顶点的情况下,随意两点之间的最短路程更新为:

通过上图我们发现:在仅仅通过1号顶点中转的情况下。3号顶点到2号顶点(e[3][2])、4号顶点到2号顶点(e[4][2])以及4号顶点到3号顶点(e[4][3])的路程都变短了。

接下来继续求在仅仅同意经过1和2号两个顶点的情况下随意两点之间的最短路程。怎样做呢?我们须要在仅仅同意经过1号顶点时随意两点的最短路程的结果下,再推断假设经过2号顶点能否够使得i号顶点到j号顶点之间的路程变得更短。

即推断e[i][2]+e[2][j]是否比e[i][j]要小,代码实现为例如以下。

  1. //经过1号顶点
  2. for(i=1;i<=n;i++)
  3. for(j=1;j<=n;j++)
  4. if (e[i][j] > e[i][1]+e[1][j])  e[i][j]=e[i][1]+e[1][j];
  5. //经过2号顶点
  6. for(i=1;i<=n;i++)
  7. for(j=1;j<=n;j++)
  8. if (e[i][j] > e[i][2]+e[2][j])  e[i][j]=e[i][2]+e[2][j];

在仅仅同意经过1和2号顶点的情况下,随意两点之间的最短路程更新为:

通过上图得知,在相比仅仅同意通过1号顶点进行中转的情况下。这里同意通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。

同理,继续在仅仅同意经过1、2和3号顶点进行中转的情况下,求随意两点之间的最短路程。

随意两点之间的最短路程更新为:

最后同意通过全部顶点作为中转,随意两点之间终于的最短路程为:

整个算法过程尽管说起来非常麻烦,可是代码实现却非常easy,核心代码仅仅有五行:

  1. for(k=1;k<=n;k++)
  2. for(i=1;i<=n;i++)
  3. for(j=1;j<=n;j++)
  4. if(e[i][j]>e[i][k]+e[k][j])
  5. e[i][j]=e[i][k]+e[k][j];

这段代码的基本思想就是:最開始仅仅同意经过1号顶点进行中转,接下来仅仅同意经过1和2号顶点进行中转……同意经过1~n号全部顶点进行中转,求随意两点之间的最短路程。用一句话概括就是:从i号顶点到j号顶点仅仅经过前k号点的最短路程。事实上这是一种“动态规划”的思想,关于这个思想我们将在《啊哈。算法2——伟大思维闪耀时》在做具体的讨论。

以下给出这个算法的完整代码:

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int e[10][10],k,i,j,n,m,t1,t2,t3;
  5. int inf=99999999; //用inf(infinity的缩写)存储一个我们觉得的正无穷值
  6. //读入n和m。n表示顶点个数,m表示边的条数
  7. scanf("%d %d",&n,&m);
  8. //初始化
  9. for(i=1;i<=n;i++)
  10. for(j=1;j<=n;j++)
  11. if(i==j) e[i][j]=0;
  12. else e[i][j]=inf;
  13. //读入边
  14. for(i=1;i<=m;i++)
  15. {
  16. scanf("%d %d %d",&t1,&t2,&t3);
  17. e[t1][t2]=t3;
  18. }
  19. //Floyd-Warshall算法核心语句
  20. for(k=1;k<=n;k++)
  21. for(i=1;i<=n;i++)
  22. for(j=1;j<=n;j++)
  23. if(e[i][j]>e[i][k]+e[k][j] )
  24. e[i][j]=e[i][k]+e[k][j];
  25. //输出终于的结果
  26. for(i=1;i<=n;i++)
  27. {
  28. for(j=1;j<=n;j++)
  29. {
  30. printf("%10d",e[i][j]);
  31. }
  32. printf("\n");
  33. }
  34. return 0;
  35. }

有一点须要注意的是:怎样表示正无穷。我们通常将正无穷定义为99999999,由于这样即使两个正无穷相加,其和仍然不超过int类型的范围(C语言int类型能够存储的最大正整数是2147483647)。在实际应用中最好预计一下最短路径的上限,仅仅须要设置比它大一点既能够。

比如有100条边,每条边不超过100的话。仅仅需将正无穷设置为10001就可以。假设你觉得正无穷和其他值相加得到一个大于正无穷的数是不被同意的话,我们仅仅需在比較的时候加两个推断条件就能够了,请注意以下代码中带有下划线的语句。

  1. //Floyd-Warshall算法核心语句
  2. for(k=1;k<=n;k++)
  3. for(i=1;i<=n;i++)
  4. for(j=1;j<=n;j++)
  5. if(e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j])
  6. e[i][j]=e[i][k]+e[k][j];

上面代码的输入数据样式为:

  1. 4 8
  2. 1 2 2
  3. 1 3 6
  4. 1 4 4
  5. 2 3 3
  6. 3 1 7
  7. 3 4 1
  8. 4 1 5
  9. 4 3 12

第一行两个数为n和m,n表示顶点个数,m表示边的条数。

接下来m行。每一行有三个数t1、t2 和t3,表示顶点t1到顶点t2的路程是t3。

得到终于结果例如以下:

通过这样的方法我们能够求出随意两个点之间最短路径。它的时间复杂度是O(N3)。令人很震撼的是它居然仅仅有五行代码,实现起来很easy。正是由于它实现起来很easy,假设时间复杂度要求不高。使用Floyd-Warshall来求指定两点之间的最短路或者指定一个点到其余各个顶点的最短路径也是可行的。

当然也有更快的算法,请看下一节:Dijkstra算法。

另外须要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图。由于带有“负权回路”的图没有最短路。比如以下这个图就不存在1号顶点到3号顶点的最短路径。由于1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这种环。最短路就会降低1。永远找不到最短路。

事实上假设一个图中带有“负权回路”那么这个图则没有最短路。

此算法由Robert W. Floyd(罗伯特·弗洛伊德)于1962年发表在“Communications of the ACM”上。

同年Stephen Warshall(史蒂芬·沃舍尔)也独立发表了这个算法。Robert W.Floyd这个牛人是朵奇葩,他原本在芝加哥大学读的文学,可是由于当时美国经济不太景气。找工作比較困难,无奈之下到西屋电气公司当了一名计算机操作员,在IBM650机房值夜班,并由此開始了他的计算机生涯。此外他还和J.W.J. Williams(威廉姆斯)于1964年共同发明了著名的堆排序算法HEAPSORT。

Robert
W.Floyd在1987年获得了图灵奖。

时间: 2024-10-09 07:03:45

仅仅有五行的Floyd最短路算法的相关文章

【啊哈!算法】算法6:只有五行的Floyd最短路算法

暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有4个城市8条公路,公路上的数字表示这条公路的长短.请注意这些公路是单向的.我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径.这个问题这也被称为“多源最短路径”问题. 现在需要一个数据结构来存储图的信息,我们仍然可以用一个4*4的矩阵(二维数组e)来存储.比如1号城市到2号城市的路程为2,则设e[1][2]

只有五行的Floyd最短路算法

暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有4个城市8条公路,公路上的数字表示这条公路的长短.请注意这些公路是单向的.我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径.这个问题这也被称为"多源最短路径"问题. 现在需要一个数据结构来存储图的信息,我们仍然可以用一个4*4的矩阵(二维数组e)来存储.比如1号城市到2号城市的路程为2,则设e[

算法6:只有五行的Floyd最短路算法

本文来自<啊哈!算法>作者啊哈磊 博客 http://ahalei.blog.51cto.com/4767671/1383613 暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有4个城市8条公路,公路上的数字表示这条公路的长短.请注意这些公路是单向的.我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径.这个问题这也被称为“多源最短路径”问题. 现在需要

codeforces1202B You Are Given a Decimal String... 记忆化搜索或Floyd最短路算法

网址:http://codeforces.com/problemset/problem/1202/B 题意: 这个题真的挺绕的,草(中日双语),就是给出一串序列,然后你可以往里面填数,使得填数后的序列可以被$x-y$计数器输出.$x-y$计数器的工作方式是:初始是$0$,每一步加上$x$或者加上$y$,然后输出对$10$取余的数.输出$0<x,y<10$的$x-y$计数器下需要添加的字符数最小值,若无法满足输出$-1$. 题解: 草,比赛时靠着网上的资料一步步写出的记忆化搜索的方法,想不出正解

Floyd最短路算法的解释.

适用于有向/无向图,本质上是一个动态规划. D[k][i][j]代表经前k个结点中转,i到j的距离.可以写出方程: D[k][i][j]=min{D[k-1][i][j], D[k-1][i][k]+D[k-1][k][j]}. 比如一个路径5->1->4->2->3. k=1时,算出了5->4的最短路.k=2时,计算了4->3,k=3时,无改变.k=4时,计算5->3,k=5,无改变. 最后,算出了5->3的最短路. 观察这个过程,发现路径中的任意一个结点

【啊哈!算法】算法7:Dijkstra最短路算法

上周我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最短路”.本周来来介绍指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径”.例如求下图中的1号顶点到2.3.4.5.6号顶点的最短路径. <ignore_js_op> 与Floyd-Warshall算法一样这里仍然使用二维数组e来存储顶点之间边的关系,初始值如下. <ignore_js_op> 我们还需要用一个一维数组dis来存储1号顶点到其余各个顶点的初始路程,如下.

玄学最短路算法——Ex Floyd

Floyd再思考 ? --by ThinkofBlank 一.序言 ? Floyd,是一个十分常用的图论算法,其作用是在O(n^3)的时间内计算出全源最短路.其实现原理是利用的dp,然而,刚开始接触Floyd的时候,并没有去尝试理解,思路此算法,仅仅记了下打法就跑了,最近无聊时思考了下,得出了些有用的东西,特此来分享 ? 注意:以下说的图都是无负环的,不然,求甚么最短路... ? 当然,给出的图是联通且点的个数至少为3 二.引入 ?1.路径合成可行性 给定一个图,假设我们已知i->j的最短路径为

最短路算法 :Bellman-ford算法 &amp; Dijkstra算法 &amp; floyd算法 &amp; SPFA算法 详解

 本人QQ :2319411771   邮箱 : [email protected] 若您发现本文有什么错误,请联系我,我会及时改正的,谢谢您的合作! 本文为原创文章,转载请注明出处 本文链接   :http://www.cnblogs.com/Yan-C/p/3916281.html . 很早就想写一下最短路的总结了,但是一直懒,就没有写,这几天又在看最短路,岁没什么长进,但还是加深了点理解. 于是就想写一个大点的总结,要写一个全的. 在本文中因为邻接表在比赛中不如前向星好写,而且前向星效率并

最短路算法(dijkstra,bellman_ford,floyd)

最短路算法 dijkstra(初级的最短路算法,适合稠密图,可用邻接表优化) bool relax(int u,int v) { double tmp=max(dist[u],edge[u][v]); if(tmp<dist[v]){ dist[v]=tmp; } } void dijkstra() { memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++){ int x; double mindist=INF; for(int j=0;j<n;j