通过对于Djkstra的学习,大致了解了最短路径问题,但是Djsktra算法由于其操作过程上的不足,不能处理带有负权边的情况,这个算法,Bellman-Ford【这些名字应该怎么记。。】则可以处理带有负权的问题,其中也用到了“松弛”操作,是根据边的长度来确定的,与之前的松弛有些不同,之前或者按点,或者按边的顺序进行遍历,这个是任选一条边为起点,然后对边集进行遍历选择第二条边,再将边集遍历一遍,有些类似于prim算法选择最小生成树是对点进行的操作,但还是有不同,,两三句话讲不明白,这几个算法总感觉有点像,有点懵,把最短路结束之后要好好区分一下。。

学习资料:《啊哈!算法》相关内容【安利安利】。


处理问题:

可以处理带负权的单源最短路问题。【有向图无向图皆宜】

算法思想:

用dis数组表述源点到各个顶点的最短路径,初始时dis[源点]=0,其余最短距离皆为正无穷,用数组 u,v,w,记录边的信息,例如:第 i 条边存储在 u[ i ] ,v[ i ] ,w[ i ]中,表示顶点 u[ i ] 到顶点 v[ i ],这条边的权值为 w[ i ]【这几个数组之间的关系一定要搞明白,各个节点有编号,在dis[节点编号 ] 中存储的是从源点到该节点的最短距离;各个边也有编号,u[ i ] ,v[ i ] 内部存储边 i 的相连的两端点的编号,w[ i ] 中表示该边的权值,一定要记清楚这个,核心代码围着这几个数组转】;

if(dis[v[i]]>dis[u[i]]+w[i])dis[v[[i]]=dis[u[i]]+w[i];

看看上面这两句代码,表b示:看看能否通过 u[ i ] ->v[ i ] (权值存储在w[ i ] 中) ,使得1 号顶点到 v [ i ] 号顶点的距离变短;即1号顶点到 u[ i ] 号顶点的距离(dis[ u[ i ] )加上 u [ i ]->v[ i ] 这条边(权值为 w[ i ]) 的值是否会比原先1号顶点到v[j号顶点的距离(dis[v[]]) 要小。这一点与Dijkstra的“松弛”操作是一样的。如果我们要把所有的边都松弛一遍,则需要循环m次【边数】代码:

for(i=1;i<=m; i++)if( dis[v[i]] > dis[u[i]]+ w[i] )dis[v[i]]= dis[u[i]] + w[i] ;

把每一.条边都“松弛”一遍后,将会有什么效果呢? 现在来举个具体的例子。求下图1号顶点到其余所有顶点的最短路径。

依然用一个dis数组来存储1号顶点到所有顶点的距离。

上方右图中每个顶点旁的值【带下划线的数字】为源点【现在为1号点】到该顶点的最短路“估计值”【距离】,即数组dis中对应的值。根据边给出的顺序,先来处理第1条边 “2  3  2 ” 【2号节点到3号节点权值为2的边,通过这条边进行松弛】,即判断dis[3]是否大于dis[2]+2。 此时dis[3]是∞, dis[2]是∞,因此dis[2]+2也是∞,所以通过“ 2 3 2 ”这条边不能使dis[3]的值变小,松池失败。
        同理,继续处理第2条边“1 2 -3”【1号节点到8号节点权值为-3的边】,现在有dis[2]大于dis[1]+(-3),通过这条边可以使dis[2]的值从∞变为-3,因此松驰成功。用同样的方法处理剩下的每一.条边。对所有的边松弛一遍后的结果如下。

这是从任意一点开始,将所有的边松弛一遍的结果,可以看到,一轮过后使得dis[2]和dis[5]的值变小,即1号顶到2号顶点的距离和1号顶点到5号顶点的距离都变短了。这当然是不够的,显然还有别的边没有取到真正的路径值,接下来我们需要对所有的边再进行一轮松弛,操作过程与上一轮是一样的,看看会发生什么。

在第二轮的松弛中,可以看到,现在通过“2 3 2” 【2→3 距离为2】这条边,可以使1号顶点到3 号顶点的距离(dis[3]) 变短了。【还记不记得,这条边在第一轮也松弛过,但在上一轮松弛失败了,这一轮却成功了,神奇?不神奇,因为在第一轮的松弛过后,1 号顶点到2号顶点的距离(dis[2]) 已经发生了变化,这一轮再通过“2 3 2” 【2→3 距离为2】这条边进行松弛的时候,就可以使1号顶点到3号顶点的距离(dis[3]) 的值变小。

换句话说,第1轮在对所有的边进行松弛之后,得到的是从1号顶点“只能经过一条边”到达其余各顶点的最短路径长度。第2轮在对所有的边进行松弛之后,得到的是从1号顶点“最多经过两条边”到达其余各顶点的最短路径长度。】如果进行k轮的话,得到的就是1号顶点“最多经过k条边”到达其余各顶点的最短路径长度。问题来了:需要进行多少轮?
       答案是:需要进行n-1轮。因为在一一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1边。【真的最多只能包含n-1条边?最短路径中不可能包含回路吗?恩,,不可能!最短路径肯定是一个不包含回路的简单路径。why?回路分为正权回路(即回路权值之和为正)和负权回路(即回路权值之和为负)。为什么这两种回路都不可能有?来讨论一下:如果最短路径中包含正权回路,那么去掉这个回路,一定可以得到更短的路径。如果最短路径中包含负权回路,那么肯定没有最短路径,因为每多走一次负权回路就可以得到更短的路径。因此,最短路径肯定是一个不包含回路的简单路径,即最多包含n-1条边,所以进行n-1轮松弛就可以了。】

Ok,解决完这个为什么第一轮不可以但第二轮就可以的神奇边的问题,接着进行第3轮和第4轮松弛操作,这里只需进行4轮就可以了,因为这个图只有5个顶点。

有些特别特别爱思考的同学又会有一个疑问,这里貌似不用进行第4轮嘛,因为进行第4轮之后dis 数组没有发生任何变化,其实是最多进行n-1轮松弛。
       整个Bellman-Ford算法用-句话概括就是:对所有的边进行n-1次“松弛”操作。核心代码只有4行,如下。

for (k=1;k<=n-1;k++) //进行n-1轮松弛for(i=1;i<=m;i++) //枚举每一条边if( dis[v[i]] > dis[u[i]] + w[i]) //尝试对每一 条边进行松弛dis[v[i]]=dis[u[i]] + w[i];

Ok,总结一下。因为最短路径上最多有n-1条边,因此Bellman-Ford算法最多有n-1 个阶段。在每-一个阶段,我们对每一条边都要执行松弛操作。其实每实施一次松弛操作, 就会有一些顶点已经求得其最短路,即这些顶点的最短路的“估计值”变为“确定值”。此后这些顶点的最短路的值就会--直保持不变,不再受后续松弛操作的影响(但是,每次还是会判断是否需要松弛,这里浪费了时间,是否可以优化呢? )。在前k个阶段结束后,就已经找出了从源点发出“最多经过k条边”到达各个顶点的最短路。直到进行完n-1个阶段后,便得出了最多经过n -1条边的最短路。

Bellman-Ford算法的完整的代码如下:

#include <stdio.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
int main(){int dis[10] ,i,k,n,m,u[10],v[10],w[10] ;int inf=99999999;//用inf(infinity的缩写)存储一个我们认为的正无穷值//读入n和m, n表示顶点个数,m表示边的条数scanf("%d%d",&n,&m);//读入边for(i=1;i<=m;i++)scanf("%d %d %d",&u[i] ,&v[i],&w[i]);//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程for(i=1;i<=n;i++)dis[i]=inf;dis[1]=0;//Bellman-Ford算法核心语句for(k=1;k<=n-1;k++)for(i=1;i<=m;i++)if( dis[v[i]]> dis[u[i]] + w[i] )dis[v[i]]=dis[u[i]]+ w[i] ;//输出最终的结果for(i=1;i<=n;i++)printf("%d ",dis[i]);getchar();getchar();return 0;
}/*
可以輸入以下数据迸行驗正。
第一行两个整数 n m。
n表示頂点个数(頂点編号为l~N),m表示边的条数。
接下来m行表示,毎行有3个数xyz。
表示从頂点x到頂点y的边的权值为z。5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3送行結果是:
0 -3 -12 4
*/

此外,Bellman-Ford算法还可以检测一- 个图是否含有负权回路。如果在进行n-1轮松弛之后,仍然存在可满足:

if( dis[v[i]]> dis[u[i]]+ w[i]dis[v[i]] = dis[u[i]] + w[i];

的情况,也就是说在进行n-1轮松弛后,仍然可以继续成功松弛,那么此图必然存在负权回路。在之前的证明中我们已经讨论过,如果一个图如果没有负权回路,那么最短路径所包含的边最多为n-1条,即进行n-1轮松弛之后最短路不会再发生变化。如果在n-1轮松弛之后最短路仍然会发生变化,则该图必然存在负权回路,关键代码如下:

//Bellman Ford算法核心语句
for(k=1;k<=n-1;k++)for(i=1;i<=m;i++)if( dis[v[i]] > dis[u[i]]+ w[i]) flag=1;dis[v[i]] = dis[u[i]]+ w[i] ;
//检测负权回路
flag=0;
for(i=1;i<=m;i++)if( dis[v[i]] > dis[u[i]] + w[i] )  flag=1;if (flag==1) printf("此图含有负权回路");

Bellman-Ford 算法的时间复杂度是O(NM),这个时间复杂度貌似比Djkstra 算法还要高,其实还可以对其进行优化。在实际操作中,Bellman- Ford算法经常会在未达到n- -1轮松弛前就已经计算出最短路,之前已经说过,n-1其实是最大值。因此可以添加一个一维数组用来备份数组dis。如果在新一轮的松弛中数组dis没有发生变化,则可以提前跳出循环,代码如下。

#include <stdio.h>
int main(){int dis[10],bak[10],i,k,n,m,u[10],v[10],w[10],check,flag;int inf=99999999;//用inf(infinity的缩写)存储一个我们认为的正无穷值//读入n和m, n表示顶点个数,m表示边的条数scanf("%d%d",&n,&m);//读入边for(i=1;i<=m;i++)scanf("%d %d %d",&u[i] ,&v[i],&w[i]);//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程for(i=1;i<=n;i++)dis[i]=inf;dis[1]=0;//Bellman-Ford算法核心语句for(k=1;k<=n-1;k++) {//将dis数组备份至bak数组中for(i=1;i<=n;i++) bak[i]=dis[i];//进行一轮松弛for(i=1;i<=m;i++)if( dis[v[i]] > dis[u[i]]+ w[i] )dis[v[i]] = dis[u[i]] + w[i];//松弛完毕后检测dis数组是否有更新check=0;for(i=1;i<=n;i++) if( bak[i]!=dis[i] ) {check=1;break;}if(check==0) break; //如果dis数组没有更新,提前退出循环结束算法}//检测负权回路flag=0;for(i=1;i<=m;i++)if( dis[v[i]]>dis[u[i]]+w[i]) flag=1;if (flag==1) printf ("此图含有负权回路");else{//输出最终的结果for(i=1;i<=n;i++)printf("%d ",dis[i]);}getchar() ; getchar() ;return 0;
}

【Bellman-Ford算法的另外一种优化在文中已经有所提示:在每实施一次松弛操作后, 就会有一些顶点已经求得其最短路,此后这些顶点的最短路的估计值就会一直保持不变, 不再受后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。这就启发我们:每次仅对最短路估计值发生变化了的顶点的所有出边执行松弛操作。Bellman-Ford还可以用队列优化,比较庆幸前两天将队列补了补】

Goodmorning,Goodnight~

Bellman-Ford算法学习笔记【最短路径(负权边)】相关推荐

  1. bellman ford 算法 判断是否存在负环

    Flyer 目录视图 摘要视图 订阅 微信小程序实战项目--点餐系统        程序员11月书讯,评论得书啦        Get IT技能知识库,50个领域一键直达 关闭 bellman for ...

  2. Bellman Ford算法详解

    一.用途 1. Bellman Ford算法是解决拥有负权边最短路问题的方法之一.还有一种方法是SPFA算法. 2. 二者相比,SPFA算法在效率方面是优于Bellman Ford算法的.但在某些情况 ...

  3. Bellman ford算法(贝尔曼·福特算法)

    Bellman - ford算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小.其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无 ...

  4. bellman - ford算法c++

    (最短路III)bellman - ford算法(适用于含负权边的图) 注意:用该算法求最短路,在有边数限制的情况下可以存在负权回路!且对所走的边的数量有要求时只能用该算法实现! 解析:因为如果没有边 ...

  5. 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配

    数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...

  6. Bellman——Ford算法

    Bellman--Ford 算法介绍 思路讲解 案例分析与代码实现 案例分析 代码实现 优先队列优化(SPFA) 优化原理 代码实现 算法介绍 我们知道Dijkstra算法只能用来解决正权图的单源最短 ...

  7. Python最优化算法学习笔记(Gurobi)

    微信公众号:数学建模与人工智能 github地址:https://github.com/QInzhengk/Math-Model-and-Machine-Learning Python最优化算法学习笔 ...

  8. bellman ford 算法

    Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的.这时候,就需要使用其他的算法来求解最短 ...

  9. LMS与RLS算法学习笔记

    LMS与RLS算法学习笔记 一. 研究目的 1.1最陡下降法理论 1.2$LMS$算法 1.3$RLS$算法 1.4研究目标 二.代码解析 三.结果 实现代码点击 这里下载 一. 研究目的 1.1最陡 ...

最新文章

  1. usaco Mixing Milk
  2. 车牌识别--Towards End-to-End License Plate Detection and Recognition: A Large Dataset and Baseline
  3. 计算机操作员中级操作试题,中级计算机操作员应知模拟考试试题(一)
  4. (1)谷歌2011校园招聘:
  5. 调整Tomcat上的参数提高性能[转]
  6. 推荐给开发人员的实用命令行工具
  7. 西宁a货翡翠,孝感a货翡翠
  8. 【C++】VS2010将写好的程序打包成安装文件发布
  9. 专业即时通讯工具的SEO人生发力
  10. php包含文件不存在,PHP包含文件错误,服务器有该文件,直接访问提示不存在
  11. go结构体初始化_golang中结构体的初始化方法(new方法) | 学习笔记
  12. 谷歌的诀窍:如何取消验证码
  13. bootstrap datetimepicker 位置错误
  14. 【爬虫】爬取当当网的图书信息
  15. 用word写接口文档时的常规格式设置
  16. 有软件测试台式电脑电源供电不足吗,电脑主机等电源供电不足的症状
  17. Linux安装配置Tomcat 8.5.34 快捷启动,关闭(二)
  18. maven(视频学习)
  19. 出货量差距大幅缩短,四季度小米可望彻底击败华为!
  20. 理解DALL·E 2, Stable Diffusion和 Midjourney工作原理

热门文章

  1. 2021年中国外商投资企业进出口总值及省市分布:进出口总值广东省位于首位[图]
  2. JetBrains - 目录
  3. 2021年终奖调查报告出炉,原来这么多人都没有?
  4. 工具收集 - D盾防火墙
  5. 删除已删软件存留在注册表中的残留信息
  6. springboot“自律”健身房会员管理系统 毕业设计-附源码456466
  7. bitset 优化dp
  8. 软件测试练手项目,可以写进简历里面的(银行:金融:商城:外卖等等)
  9. 数据库设计三范式详细介绍--数据库设计规范之数据库设计三范式
  10. Failed to convert 、 to ACE; string contains a disallowed character