暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。
 
 
        上图中有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 = ; i <= n; i++)
{
for (j = ; j <= n; j++)
{
if (e[i][j] > e[i][] + e[][j])
e[i][j] = e[i][] + e[][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=;i<=n;i++)
for(j=;j<=n;j++)
if (e[i][j] > e[i][]+e[][j]) e[i][j]=e[i][]+e[][j];
//经过2号顶点
for(i=;i<=n;i++)
for(j=;j<=n;j++)
if (e[i][j] > e[i][]+e[][j]) e[i][j]=e[i][]+e[][j];
 
在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:
        通过上图得知,在相比只允许通过1号顶点进行中转的情况下,这里允许通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。
 
        同理,继续在只允许经过1、2和3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:
        最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:
        整个算法过程虽然说起来很麻烦,但是代码实现却非常简单,核心代码只有五行:

     for(k=;k<=n;k++)
for(i=;i<=n;i++)
for(j=;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[][],k,i,j,n,m,t1,t2,t3;
int inf=; //用inf(infinity的缩写)存储一个我们认为的正无穷值
//读入n和m,n表示顶点个数,m表示边的条数
scanf("%d %d",&n,&m);
//初始化
for(i=;i<=n;i++)
for(j=;j<=n;j++)
if(i==j) e[i][j]=;
else e[i][j]=inf;
//读入边
for(i=;i<=m;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
//Floyd-Warshall算法核心语句
for(k=;k<=n;k++)
for(i=;i<=n;i++)
for(j=;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j] )
e[i][j]=e[i][k]+e[k][j];
//输出最终的结果
for(i=;i<=n;i++)
{
for(j=;j<=n;j++)
{
printf("%10d",e[i][j]);
}
printf("\n");
}
return ;
}
 
        另外需要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图,因为带有“负权回路”的图没有最短路。例如下面这个图就不存在1号顶点到3号顶点的最短路径。因为1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这样的环,最短路就会减少1,永远找不到最短路。其实如果一个图中带有“负权回路”那么这个图则没有最短路。
 

Floyd-傻子也能看懂的弗洛伊德算法(转)的更多相关文章

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

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

  2. Floyd-蒟蒻也能看懂的弗洛伊德算法(当然我是蒟蒻)

    今天来讲点图论的知识,来看看最短路径的一个求法(所有的求法我以后会写,也有可能咕咕咕) 你们都说图看着没意思不好看,那今天就来点情景             暑假,_GC准备去一些城市旅游.有些城市之 ...

  3. C--全排列的实现(递归方法) 傻子也能看懂的

      假设数组含有n个元素,则提取数组中的每一个元素做一次头元素,然后全排列除数组中除第一个元素之外的所有元素,这样就达到了对数组中所有元素进行全排列的得目的.[这句话才是重点!] 比如 1,2,3.的 ...

  4. 萌新也能看懂的KMP算法

    前言 算法是什么?算法就是数学规律.怎么去总结和发现这个规律,就是理解算法的过程. KMP算法的本质是穷举法,而并不是去创造新的匹配逻辑. 以下将搜寻的字符串称为子串(part),以P表示.被搜寻的字 ...

  5. 弗洛伊德算法(Floyd )

    package com.rao.graph; /** * @author Srao * @className Floyd * @date 2019/12/11 18:43 * @package com ...

  6. Floyd弗洛伊德算法

    先看懂如何使用 用Java实现一个地铁票价计算程序 String station = "A1 A2 A3 A4 A5 A6 A7 A8 A9 T1 A10 A11 A12 A13 T2 A1 ...

  7. [转]看懂UML类图

    这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图所表达的含义和最终的代码 ...

  8. 看懂Oracle执行计划

    最近一直在跟Oracle打交道,从最初的一脸懵逼到现在的略有所知,也来总结一下自己最近所学,不定时更新ing- 一:什么是Oracle执行计划? 执行计划是一条查询语句在Oracle中的执行过程或访问 ...

  9. 一张图看懂ANSYS17.0 流体 新功能与改进

    一张图看懂ANSYS17.0 流体 新功能与改进   提交 我的留言 加载中 已留言   一张图看懂ANSYS17.0 流体 新功能与改进 原创2016-02-03ANSYS模拟在线模拟在线 模拟在线 ...

随机推荐

  1. UDP网络程序,客户端和服务端交互原理

    创建一个udp客户端程序的流程是简单,具体步骤如下: 创建客户端套接字 发送/接收数据 关闭套接字 UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实 ...

  2. react框架的状态管理

    安装: cnpm install --save redux cnpm install --save react-redux   安装好后导入模块内容: impor {createStore} from ...

  3. android 内存泄漏,以及检测方法

    1.为什么会产生内存泄漏 当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏. 2.内 ...

  4. b站评论爬取

    var userData = {}; var startPage = 0; var endPage = 0; var startTime = ""; var endTime = & ...

  5. python位运算之计算中位数

    # -*- coding: utf-8 -*- # @Time : 2018/11/23 10:49 PM # @Author : cxa # @File : 1.py # @Software: Py ...

  6. KVM -> 虚拟机管理&console登录_02

    1.KVM虚拟机管理操作 virsh命令常用参数总结 1.开机关机: virsh list (只可以查看运行的虚拟机) virsh list  --all (全部都可以查看) 开机与关机: virsh ...

  7. saltstack自动化运维系列③之saltstack的常用模块使用

    saltstack自动化运维系列③之saltstack的常用模块使用 1.命令的常用方法: 指定主机运行命令 # salt 'mini1' cmd.run 'date'mini1: Fri Apr 7 ...

  8. 转载:Java中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  9. webstrom随手笔记

    1.node代码智能提示功能: 2.页面代码格式化快捷键: Ctrl+Alt+L键,或者Code =>Rearrange code

  10. Oracle 数据库逻辑结构

    注:本文来源于 <腾科OCP培训课堂>.非准许商业活动. Oracle 数据库逻辑结构 一.存储关系 Oracle 数据库逻辑上是由一个或多个表空间组成的,表空间物理上是由一个或多个数据 ...