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

上图中有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循环,代码实现如下。

for (i = 1; i <= n; i++)
{for (j = 1; j <= n; j++){if (e[i][j] > e[i][1] + e[1][j])e[i][j] = e[i][1] + e[1][j];}
}

在只允许经过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号顶点
for(i=1;i<=n;i++)for(j=1;j<=n;j++)if (e[i][j] > e[i][1]+e[1][j])  e[i][j]=e[i][1]+e[1][j];
//经过2号顶点
for(i=1;i<=n;i++)for(j=1;j<=n;j++)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号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:

最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:

整个算法过程虽然说起来很麻烦,但是代码实现却非常简单,核心代码只有五行:

for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(e[i][j]>e[i][k]+e[k][j])e[i][j]=e[i][k]+e[k][j];

这段代码的基本思想就是:最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,求任意两点之间的最短路程。用一句话概括就是:从i号顶点到j号顶点只经过前k号点的最短路程。

#include
int main()
{int e[10][10],k,i,j,n,m,t1,t2,t3;int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值//读入n和m,n表示顶点个数,m表示边的条数scanf("%d %d",&n,&m);//初始化for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i==j) e[i][j]=0;else e[i][j]=inf;//读入边for(i=1;i<=m;i++){scanf("%d %d %d",&t1,&t2,&t3);e[t1][t2]=t3;}//Floyd-Warshall算法核心语句for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(e[i][j]>e[i][k]+e[k][j] )e[i][j]=e[i][k]+e[k][j];//输出最终的结果for(i=1;i<=n;i++){for(j=1;j<=n;j++)printf("%10d",e[i][j]);printf("\n");}return 0;
}

另外需要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图,因为带有“负权回路”的图没有最短路。例如下面这个图就不存在1号顶点到3号顶点的最短路径。因为1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这样的环,最短路就会减少1,永远找不到最短路。其实如果一个图中带有“负权回路”那么这个图则没有最短路。

迪杰斯特拉算法要求所有边的权值均非负,弗洛伊德算法允许图中有带负权值的边,但不允许有包含带负权值的边组成的回路。

Floyd-傻子也能看懂的弗洛伊德算法相关推荐

  1. 最短路径算法 Floyd-傻子也能看懂的弗洛伊德算法(转)

    讲的清楚的1 https://www.cnblogs.com/wangyuliang/p/9216365.html 讲的清楚的2 ahttps://zhuanlan.zhihu.com/p/13911 ...

  2. Floyd-傻子也能看懂的弗洛伊德算法(转)

    暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. 上图中有4个城市8条公路,公路上的数字表 ...

  3. 我这种傻子都能看懂的 java oi流

    此文章较为适合我这种初学者 废话不多说直接进入正题 观看如下代码 public class 铜中 {public static void main(String []args) {File yf=ne ...

  4. 人人都能看懂的EM算法推导

    作者丨August@知乎(已授权) 来源丨https://zhuanlan.zhihu.com/p/36331115 编辑丨极市平台 估计有很多入门机器学习的同学在看到EM算法的时候会有种种疑惑:EM ...

  5. em算法 实例 正态分布_人人都能看懂的EM算法推导

    ↑ 点击蓝字 关注极市平台作者丨August@知乎(已授权)来源丨https://zhuanlan.zhihu.com/p/36331115编辑丨极市平台 极市导读 EM算法到底是什么,公式推导怎么去 ...

  6. 争取能让大家都能看懂的 DFA 算法

    为什么要学习这个算法 我们公司一直都有的一个敏感词检测服务,前一段时间遇到了瓶颈,因为词库太多了导致会有一些速度过慢,而且一个正则表达式已经放不下了,需要进行拆分正则才可以. 正好我以前看过有关 df ...

  7. 傻子都能看懂的 —— 详解欧拉公式推导

    导语 不论是高等数学还是大学物理,欧拉公式都如影随形.因为其重要性和划时代意义,Euler Formula(欧拉公式)有着很多了不起的别称,例如"上帝公式"."最伟大的数 ...

  8. 傻子都能看懂的马拉车Manacher

    Manacher's Algorithm 马拉车算法操作及原理 package advanced_001;public class Code_Manacher {public static char[ ...

  9. 傻子都能看懂的SVM

    文章目录 1.SVM简介 1-1.物理世界角度理解svm 2.知识前提 2-1 数学基本知识 2-2 几何间隔与函数间隔 2-3 拉格朗日乘子法与KKT条件 2-3-1 什么是拉格朗日乘子法 2-3- ...

最新文章

  1. js下载文件及命名(兼容多浏览器)
  2. 【GitHub】GitHub 的 Pull Request 和 GitLab 的 Merge Request 有区别吗?
  3. CentOSLinux安装Docker容器
  4. C++set容器-插入和删除
  5. 使用ActiveMQ Artemis在两个WildFly服务器之间构建水平JMS桥
  6. Spring Boot(19)---开发Web应用之Thymeleaf篇
  7. dYdX 2020年度回顾:累计交易量增长40倍
  8. [ZT]ASP.NET中如何防范SQL注入式攻击
  9. 数据分析必备算法(算数平均值,加权平均值,最值,中位数,标准差,时间数据处理 ,数组的轴向汇总, 移动均线 ,卷积(简单概念))
  10. 维纳滤波法matlab代码,完整的维纳滤波器Matlab源程序
  11. 张学友1/2世纪演唱会巡演日程、曲目及新闻汇总
  12. 选择婚庆公司的注意事项
  13. AN蜘蛛池V2.0附搜索留痕转码工具SEO工具
  14. 如何使用chatGPT生成小红书种草文案
  15. 重装解决一切 Linux:unknown filesystem
  16. python红楼梦人物词频统计_通过数据挖掘能分析《红楼梦》各回的真伪吗?
  17. 基于akshare查取每天五分钟成交额,35分钟成交额,1h成交额,并输出为cvs
  18. Mysql数据库——DQL数据查询语言
  19. oracle中怎样获取当前月上个月的第一天和最后一天?
  20. 怎样去除桔梗导航劫持主页

热门文章

  1. Sklearn中的CV与KFold详解
  2. 奇奇怪怪的小姿势——关于UI的各种Position
  3. 关于SCI的那些事儿
  4. Linux grep
  5. Python3.5 Day1作业:实现用户密码登录,输错三次锁定。
  6. 【转】 指针函数与函数指针的区别
  7. CDOJ 1070 秋实大哥打游戏 带权并查集
  8. [转]SpringMVC+Hibernate+Spring 简单的一个整合实例
  9. 我的第一篇blog 【随意】
  10. MiniGUI细节处理(转)