warshall算法求传递闭包c++_【建模小课堂】图论算法

0f1e3102a3f837524835f53a3157b8e2.png

图论算法

图论算法在计算机科学中扮演着很重要的角色,它提供了对很多问题都有效的一种简单而系统的建模方式。很多问题都可以转化为图论问题,然后用图论的基本算法加以解决。这类问题算法主要包括Dijkstra,Floyd,Prim,最短路、网络流、二分图等算法。

0f1e3102a3f837524835f53a3157b8e2.png 背景 0f1e3102a3f837524835f53a3157b8e2.png

哥尼斯堡(KOnigsberg)七桥问题

在哥尼斯堡的一个公园里,有七座桥将河中两个岛及岛与河岸连接起来,问是否可能从这四块陆地中任一块出发,恰好通过每座桥一次,再回到起点

8b8cceac536c7c9b378906bf14fd34d2.png

1736年,在经过一年的研究之后,29岁的欧拉提交了《哥尼斯堡七桥》的论文,圆满解决了这一问题,同时开创了数学新一分支---图论。

七桥问题在论文中,欧拉将七桥问题抽象出来,把每一块陆地考虑成一个点,连接两块陆地的桥以线表示。并由此得到了如图一样的几何图形。 若我们分别用A、B、C、D四个点表示为哥尼斯堡的四个区域。这样著名的“七桥问题”便转化为是否能够用一笔不重复地画出过此七条线的问题了。若可以画出来,则图形中必有终点和起点,并且起点和终点应该是同一点,由于对称性可知由B或C为起点得到的效果是一样的,若假设以A为起点和终点,则必有一离开线和对应的进入线,若我们定义进入A的线的条数为入度,离开线的条数为出度,与A有关的线的条数为A的度,则A的出度和入度是相等的,即A的度应该为偶数。即要使得从A出发有解则A的度数应该为偶数,而实际上A的度数是5为奇数,于是可知从A出发是无解的。同时若从B或D出发,由于B、D的度数分别是3、3,都是奇数,即以之为起点都是无解的。上述理由可知,对于所抽象出的数学问题是无解的,即“七桥问题”也是无解的。
由此我们得到:欧拉回路关系

(对于一个连通图,通常把从某结点出发一笔画成所经过的路线叫做欧拉路。人们又通常把一笔画成回到出发点的欧拉路叫做欧拉回路。具有欧拉回路的图叫做欧拉图。)


可知要使得一个图形可以一笔画,必须满足如下两个条件:
1. 图形必须是连通的。
2. 图中的“奇点”个数是0或2。

0f1e3102a3f837524835f53a3157b8e2.png 引进概念 0f1e3102a3f837524835f53a3157b8e2.png

图:

二元组G(V,E)称为图(graph)。V为顶点(vertex) 或结点(node)的集。E为图中边的集合。

•点用数字0...n-1表示

a76f1826eb1506628ebd185a9cfa0b73.png

•点对(u,v)称为边(edge)或称弧
•子图(subgraph):边的子集和相关联的点集

•带权图:可以给边加权(weight),成为带权图,或加权图(weightedgraph).权通常代表费用、距离等,可以是正数,也可以是负数

•有向图:边都是单向(unidirectional)的,因此边(u,v)是有序数对.(最基本的图通常被定义为“无向图”,与之对应的则被称为“有向图”。两者的区别在于,有向图中的边是有方向性的。下图即是一个有向图,边的方向分别是:(1->2), (1-> 3), (3-> 1), (1->5), (2->3), (3->4), (3->5), (4->5), (1->6), (4->6)。

注意,图中的边(1->3)和(3->1)是不同的。有向图和无向图的许多原理和算法是相通的。)
有时用弧(arc)专指有向边 

45ef960eafc4e2595b5bbe28da0fee52.png

有时用弧(arc)专指有向边

•一条路径(path)是一个结点序列,路上的相邻结点在图上是邻接的
•如果结点和边都不重复出现,则称为简单路径(simplepath).如果除了起点和终点相同外没有重复顶点和边,称为圈(cycle).
•不相交路(disjoint path)表示没有除了起点和终点以外的公共点的路.更严格的全图:N个顶点的图,有N(N-1)/2个节点于(u,v),若邻接则改为非邻接,若非邻接则改为邻接,得到的图原图的补图

生成树:
•树:N个点,N-1条边的连通图(无环连通图)
•生成树:包含某图G所有点的树
•一个图G是树当且仅当以下任意一个条件成立
   G有V-1条边,无圈

   G有V-1条边,连通
   任意两点只有唯一的简单路径
   G连通,但任意删除一条边后不连通

0f1e3102a3f837524835f53a3157b8e2.png 最短路问题 0f1e3102a3f837524835f53a3157b8e2.png

对一幅图G,我们对每一条边赋权w(e),成为一个赋权图。H是G的一个子图,则W(H) = sigma(w(e)),也就是对每条边的权求和。寻找从一个点a到另一个b的一个子图,使得权和最小,即为最短路问题。

解法一:Dijkstra算法(仅适用于无负权边的情况)

把顶点集合V分成两组:•(1)S:已求出的顶点的集合(初始时只含有源点VO)•(2)V-S=T:尚未确定的顶点集合
将T中顶点按递增的次序加入到S中,保证:
•(1)从源点VO到S中其他各顶点的长度都不大于从VO到T中任何顶点的最短路径长度
•(2)每个顶点对应一个距离值
•S中顶点距离:从VO到此顶点的最短距离
•T中顶点距离:从V0到此顶点的只包括S中顶点作中间顶点的

算法图解:
不断求一个点集合中的每个点,和与他相邻点最短路的最小值。我们还是从实例出发,更容易讲解。求下面这个图从A到L的最短路。


(1)
令a1 = A(便于标记),t(a1) = 0(表示点a1到a的最短路),S={a1}(被选择的点的集合),T = 0(空集 表示被选择的最短路的边集)。

eaa8752f54ed3941bc7cda9725ae8cf8.png

(2)
求与S中的点a1与他相邻的点的距离d,取点a2 = C使得距离最小。令S={a1,a2},T={AC}。

52beb1a4f12cf4b169265932dfa18241.png

(3)
重复第二步,求S中的点a1,a2的相邻点中(去除已选择点),距离最小的那个,则为AB,CE。再取AB,CE中权和最小的一个,B,所以a3=B,令S={a1,a2,a3},T={AC,AB}。

持续下去,不断寻找,S集合中的每个点与他相邻点的最小距离,然后在这些最小距离中找到最小的那个加入到S中,同时加入相应的边。

1e723ddff1d5b9929d76e1cda3689023.png9b2e4e45ea463f853a7f6adf354cf40c.png

直到到达终点L为止。最后得到的最短路为:

e2418299b14d79f88c6c74caebdde270.png

此算法是多项式时间可求解的。

解法二:Floyd算法(插点法)
Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题。

算法思想原理:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释。从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
算法描述:
暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如左图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。

c88102d71eb7ffd5f20157d281d986f4.png

上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径。我们可以用一个4*4的矩阵(二维数组e)来存储。比如1号城市到2号城市的路程为2,则设e[1][2]的值为2。2号城市无法到达4号城市,则设置e[2][4]的值为∞。另外此处约定一个城市自己到自己的路程也是0,例如e[1][1]为0。

d71c921ecfb794d6a132f278fda24a7c.png

如果求任意两点之间的最短路径,两点之间可以直接到达但却不是最短的路径,要让任意两点(例如从顶点a点到顶点b)之间的路程变短,只能引入第三个点(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程。每个顶点都有可能使得另外两个顶点之间的路程变短。

当任意两点之间不允许经过第三个点时,这些城市之间最短路程就是初始路程。

ee7b8888e4ab0cc191664b21428c0caf.png

在只允许经过1号顶点的情况下,任意两点之间的最短路程更新为:

42c2ae428ed6e49031ab764ef7168061.png

在只允许经过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和2号两个顶点的情况下任意两点之间的最短路程。

我们需要在只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变得更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小,在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:

59a4d90571d6655c32a9a4eda415b67a.png

判断e[i][2]+e[2][j]是否比e[i][j]要小。同理,继续在只允许经过1、2和3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:

93ea6e15b36a442c0c5a1b89cedbefb5.png

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

e38b4879ee05976b4db170d8d8d71307.png

0f1e3102a3f837524835f53a3157b8e2.png 最小生成树问题 0f1e3102a3f837524835f53a3157b8e2.png

同样在一个连通赋权图中,寻找一颗生成树使得权和最小。

Kruskal算法(避圈法):
(1)在G中选取最小权边e,并置边数i=1;
(2)当i=n-1时结束,否则转向(3);
(3设已选择的边为e,e2,.,e,在G中选不同于e,e2,.,e的边e;+1,使得e;+是满足与e,e,...,e不构成回路且权值最小的边
(4)置为i+1,转向(2)。
不断寻找最小权的边即可。
比如,寻找下图的最小生成树

5aa31fa5d9950717871612fbc044eef5.png

结果如下

3625ea08201662948e06c8393b40a93b.png

0f1e3102a3f837524835f53a3157b8e2.png 网络流问题 0f1e3102a3f837524835f53a3157b8e2.png

如下图所示,有一个顶点,称为源点(source);还有一个顶点,称为汇点(sink)。对于顶点,它最大流出2,因此它的最大流入为2,如下右图所示。而他的最大流也就是5。

35cbe3f5b18e07f0d41a9d73804ff847.png

要想计算最大流,同样可是使用前面的思想——分阶段进行。令开始时所有边都没有流,如下中间图所示。我们可以用残余图(residual graph)来表示对于每条边还能再添加上多少流。对于每一条边,可以从容量中减去当前的流而计算出残留的流。

9dc778495eac3d0cb09a4dd81034b5f0.png

第一步:假设我们选择路径,此时会发出2个单位的流通过这条路径的每一边,如下中间图所示。对比左图,我们做如下约定:一旦注满(使饱和)一条边,例如到和到,就将这条边从残余图(也就是中间图)去掉,如下右图所示。

30a4a4654a9a8ba463b04e2efbb9982f.png

第二步:接下来选择路径,此时也会发出2个单位的流通过这条路径的每一边,如下中间图所示。同样将残余图更新如下右图所示。

302857dff8d84243d4a5c36f5713eeff.png

第三步:从上图的残余图中我们已经可以看出来最后一步的唯一一种走法了。

做如下图所示更新。

f03fa09d6167030e1634fb436a23cd4d.png

很显然无法走到,因此算法至此便终止了。因此正好5个单位的流是最大值。

如果一开始我们选择了如下图中间所示的走法,那么算法就会失败了,因为路已经被堵死了。

d7999ac2035497c35f1fd4916235266b.png

为了使算法得以成功运作,那么就要让流图中具有以相反方向发送流的路径,如下所示。那么对于如下右图中的残余图而言,从d返回到a的便成了3而非4,这是因为从t流到d的流量是3个单位。现在在残余图中就有a和d之间有2个方向,或者还有1个单位的流可以从导向,或者是3个单位的流导向相反的反向,当然,我们也可以撤销流。

7280c4c77be1fa90a20506ccecaa1959.png

紧接着如果通过到导入2个单位的流,算法就会从边取走2个单位的流,更新流图如下。

aa50b58b60cefad8b4b8bc87a04f2b2e.png

参考:

1.《数据结构》、《离散数学》、《算法导论》、《挑战程序设计竞赛》等书籍和必应、维基百科等网络。
2.https://blog.csdn.net/u012469987/article/details/51319574
3.https://www.cnblogs.com/hxsyl/p/3270401.html
4.https://wk.baidu.com/view/343fcfe8172ded630b1cb6f9
5.https://wk.baidu.com/view/be1b0a7f58cfa1c7aa00b52acfc789eb172d9e07

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/562616.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java定义计算机类并模拟其操作

java定义计算机类并模拟其操作 /*** 定义计算机类并模拟其操作*/ public class Computer {//成员变量private String cpu "Intel"; // cpuprivate String memory; //内存private String mainBoard; //主板private String keyBoard; //键盘//构造方法public…

java对象数组

java对象数组 /*** 对象数组*/ public class TestArray {public static void main(String[] args) {//定义一个数组&#xff0c;存储4个分数&#xff0c;并遍历int[] arr;arr new int[4];arr[0] 90;arr[1] 80;arr[2] 100;arr[3] 54;for (int score:arr){System.out.print…

引用数据类型的方法调用

引用数据类型的方法调用 /*** 引用数据类型的方法调用*/ public class TestRefArgs{public static void main(String[] args) {Point p new Point();p.x 10;p.y 20;System.out.println("交换前的值&#xff1a;"p.x"\t"p.y);swap(p);System.out.printl…

java this关键字的使用

java this关键字的使用 /*** this关键字的使用*/ public class Student {//成员变量private int sno; //学号private String name; //姓名private String sex; //性别private double score; //分数//构造方法public Student(){}public Student(int sno,String nam…

使用PE信息查看工具和Dependency Walker工具排查因为库版本不对导致程序启动报错的问题

目录 1、问题说明 2、问题分析思路 3、问题分析过程 3.1、使用Dependency Walker打开软件主程序&#xff0c;查看库与库的依赖关系&#xff0c;找出出问题的库 3.2、使用PE工具查看dll库的时间戳 3.3、解决办法 4、最后 VC常用功能开发汇总&#xff08;专栏文章列表&…

关联的两个字段度需要建立索引吗_索引那些事儿

最近的工作中进行了几个SQL优化,对索引也有了一些新的认识。 什么是索引?百度百科是这么说的: 在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据…

MySQL添加、更新、删除数据

MySQL添加、更新、删除数据 1、查询数据 select * from student; 2、添加数据 insert into student values(1,"张三","男",23,1999-12-23,Java001,zhangsanqq.com); 可以添加两条相同的数据&#xff0c;因为没有创建相应的约束constraint insert into…

登陆 manager app显示不是私密链接_小米上线了一款靠指静脉识别开锁的私密箱,打造你的私人存储空间...

印象中的保险箱体积大、价格昂贵、开锁繁琐&#xff0c;一直都认为那是企业老板、成功人士的标配&#xff0c;对于普通家庭来说存在的价值、或实用性方面并不是很高。往常遇到长时间外出旅行&#xff0c;也会担心家里重要的证件、首饰等贵重物品的安全&#xff0c;老婆选择寄存…

MySQL修改、删除表的结构

MySQL修改、删除表的结构 1、增加一列 alter table student add score double(3,1); 说明 double&#xff08;3,1&#xff09;指数据是三位&#xff0c;小数部分占一位&#xff0c;整数部分占两位&#xff0c;默认最后一列 alter table student add score double (5,2) fi…

MySQL表级完整性约束

MySQL表级完整性约束 为防止不符合规范的数据存入数据库&#xff0c;在用户对数据进行插入、修改、删除等操作时&#xff0c;MySQL提供 了一种机制来检查数据库是否满足规定的条件&#xff0c;以保证数据库中数据的准确性和一致性&#xff0c;这种机制 就是完整性约束。MySQL中…

php redis 投票_高性能Redis服务架构分析与搭建

基于内存的Redis应该是目前各种web开发业务中最为常用的key-value数据库了&#xff0c;我们经常在业务中用其存储用户登陆态(Session存储)&#xff0c;加速一些热数据的查询(相比较mysql而言&#xff0c;速度有数量级的提升)&#xff0c;做简单的消息队列(LPUSH和BRPOP)、订阅发…

MySQL表的非外键约束

MySQL表的非外键约束 /* 建立一张用来存储学生信息的表 字段包含学号、姓名、性别&#xff0c;年龄、入学日期、班级&#xff0c;email等信息 学号是主键 不能为空 ? 唯一 姓名不能为空 性别默认值是男 &#xff0c;只能取值男、女 年龄&#xff1a;18---50 Email唯一 *…

static变量 java

static 变量java static 是java中的一个关键字&#xff0c;单词本身是静态的含义。一个类的成员包含变量、方法、构造方法 、代码块和内部类&#xff0c;static可以修饰除了构造方法以外的所有成员使用static修饰的成员称为静态成员&#xff0c;是属于某个类的&#xff1b;而不…

java static方法

static方法 1、static方法的作用访问static变量和static方法2、static方法的调用方式通过类名调用、通过对象名来访问3、不可以静态方法中不可以访问非静态变量 静态方法中不可以访问非静态方法 静态方法中不可以访问this原因&#xff1a;加载类的时候就加载静态变量和静态方法…

保存验证码的方法_selenium自动化测试:6.验证码处理

0.前言如果可以的话&#xff0c;请先关注&#xff08;专栏和账号&#xff09;&#xff0c;然后点赞和收藏&#xff0c;最后学习和进步。你的支持是我继续写下去的最大动力&#xff0c;个人定当倾囊而送&#xff0c;不负众望。谢谢&#xff01;&#xff01;&#xff01;1.前提基…

非root用户组启动sftp_如何在 Debian 10 中配置 Chroot 环境的 SFTP 服务 | Linux 中国

SFTP 是最常用的用于通过 ssh 将文件从本地系统安全地传输到远程服务器的方法https://linux.cn/article-12186-1.html作者&#xff1a;Pradeep Kumar译者&#xff1a;郑SFTP 意思是“安全文件传输协议(Secure File Transfer Protocol)” 或 “SSH 文件传输协议(SSH File Transf…

java权限修饰符

java权限修饰符 java一共有四种访问控制符1、private 表示私有&#xff0c;只有自己类能访问2、default 表示没有修饰符修饰&#xff0c;只有同一个报的类能访问3、protected 表示可以被同一个包的类以及其他包中的子类访问4、public 表示可以被该项目的所有包中的所有类访问类…

java封装练习

java封装练习 /*** 以面向对象的思想&#xff0c;编写自定义类描述图书信息。设定属性包括&#xff1a;书名、作者、出版社名、价格* 方法包括&#xff1a;信息介绍** 要求&#xff1a;* 1、设置属性的私有访问权限&#xff0c;通过公有的get、set方法实现对属性的访问* 2、限…

接口自动化测试_Python自动化测试学习路线之接口自动化测试「模块四」

01.接口自动化测试概念(接口测试的定义与意义;接口测试的实现方式和接口自动化的实现流程)02.HTTP协议(通信协议原理;HTTP与HTTP协议详解(请求,响应,请求方法&#xff0c;状态码)、session、cookie、token鉴权)03.JSON数据格式(JSON与XML的比较;JSON的语法规则;JSON的数据类型)…

java继承实现

java继承实现 /*** 继承*/ public class Animal { //默认animal集成objectprivate String color;private int age;public Animal(){super();}public Animal(String color,int age){this.color color;this.age age;}public void eat(){System.out.println("吃饭"…