Bellman-Ford与SPFA
一、Bellman-Ford
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(当然也可以是无向图)。与Dijkstra相比的优点是,也适合存在负权的图。
若存在最短路(不含负环时),可用Bellman-Ford求出,若最短路不存在时,Bellman-Ford只能用来判断是否存在负环。
松弛:
每次松弛操作实际上是对相邻节点的访问(相当于广度优先搜索),第n次松弛操作保证了所有深度为n的路径最短。由于图的最短路径最长不会经过超过|V| - 1条边,所以可知贝尔曼-福特算法所得为最短路径,也可只时间复杂度为O(VE)。
负边权操作:
与迪科斯彻算法不同的是,迪科斯彻算法的基本操作“拓展”是在深度上寻路,而“松弛”操作则是在广度上寻路,这就确定了贝尔曼-福特算法可以对负边进行操作而不会影响结果。
负权环判定:
因为负权环可以无限制的降低总花费,所以如果发现第n次操作仍可降低花销,就一定存在负权环。
基本操作:
- 创建源顶点 v 到图中所有顶点的距离的集合 distSet,为图中的所有顶点指定一个距离值,初始均为 Infinite,源顶点距离为 0;
- 计算最短路径,执行 V - 1 次遍历;
- 对于图中的每条边:如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d;
- 检测图中是否有负权边形成了环,遍历图中的所有边,计算 u 至 v 的距离,如果对于 v 存在更小的距离,则说明存在环(无向图不能用这种方法判断负环)
正确性:
Bellman-Ford 算法采用动态规划进行设计,实现的时间复杂度为 O(V*E),其中 V 为顶点数量,E 为边的数量。简单的说我们用
dis[k][v]表示经过前i个顶点到达v的最短路,易得转移方程dis[k][v] = min(dis[k][v],dis[ k -1][u] + w)。未使用滚动数组优化空间时,实现的代码如下:
int dis[maxv][maxv]; //dis[k][v];表示选取前k个时到达i的最短距离
struct Edge
{
int u, v, w;
}edge[maxv];
int n, m; void Bellman_Ford(int s)
{
memset(dis, INF, sizeof(dis));
for (int i = ; i <= n; i++) dis[i][s] = ;
for (int k = ; k <= n - ; k++)
for (int i = ; i < m; i++)
{
int u = edge[i].u, v = edge[i].v, w = edge[i].w;
dis[k][v] = min(dis[k][v], dis[k - ][u] + w);
}
}
优化:
循环的提前退出:
在实际操作中,贝尔曼-福特算法经常会在未达到 |V| - 1 次前就出解,|V| -1 其实是最大值。于是可以在循环中设置判定,在某次循环不再进行松弛时,直接退出循环,进行负权环判定。
队列优化:
即SPFA
二、SPFA
是一个用于求解有向带权图单源最短路径的改良的贝尔曼-福特算法(当然也可以通过将每条边换为两条逆向的边来用于无向图)。这一算法被认为在随机的稀疏图上表现出色,并且极其适合带有负边权的图。然而SPFA在最坏情况的时间复杂度与Bellman-Ford算法相同,因此在非负边权的图中仍然最好使用Dijkstra。
原理:
基于Bellman-Ford之外,再可以确定,松弛操作必定只会发生在最短路径前导节点松弛成功过的节点上,用一个队列记录松弛过的节点,可以避免了冗余计算。
优化:
SPFA算法的性能很大程度上取决于用于松弛其他节点的备选节点的顺序。我们注意到其与Dijkstra很像,一方面,优先队列替换成普通的FIFO队列,而另一方面,一个节可以多次进入队列点。
事实上,如果 q 是一个优先队列,则这个算法将极其类似于戴克斯特拉算法。然而尽管这一算法中并没有用到优先队列,仍有两种可用的技巧可以用来提升队列的质量,并且借此能够提高平均性能(但仍无法提高最坏情况下的性能)。两种技巧通过重新调整 q 中元素的顺序从而使得更靠近源点的节点能够被更早地处理。
距离小者优先(Small Lable First(SLF)):
将总是把v压入队列尾端改为比较dis[v]与dis[q.front()]的大小(为了避免出现队列为空的操作,先将v压入队尾),并且在v较小时将v压入队列的头端。

距离大者优先(Large Lable Last(LLL)):
我们更新队列以确保队列头端的节点的距离总小于平均,并且任何距离大于平均的节点都将被移到队列尾端。

改为DFS版:
dfs版spfa判环根据:若一个节点出现2次及以上,则存在负环。具有天然的优势。由于是负环,所以无需像一般的spfa一样初始化为极大的数,只需要初始化为0就够了(可以减少大量的搜索,但要注意最开始时for一遍)。
Bellman-Ford与SPFA的更多相关文章
- ACM/ICPC 之 最短路径-Bellman Ford范例(POJ1556-POJ2240)
两道Bellman Ford解最短路的范例,Bellman Ford只是一种最短路的方法,两道都可以用dijkstra, SPFA做. Bellman Ford解法是将每条边遍历一次,遍历一次所有边可 ...
- poj1860 bellman—ford队列优化 Currency Exchange
Currency Exchange Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 22123 Accepted: 799 ...
- uva 558 - Wormholes(Bellman Ford判断负环)
题目链接:558 - Wormholes 题目大意:给出n和m,表示有n个点,然后给出m条边,然后判断给出的有向图中是否存在负环. 解题思路:利用Bellman Ford算法,若进行第n次松弛时,还能 ...
- 数据结构与算法--最短路径之Bellman算法、SPFA算法
数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...
- Bellman—Ford算法思想
---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G ...
- Bellman - Ford 算法解决最短路径问题
Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力 ...
- Dijkstra算法与Bellman - Ford算法示例(源自网上大牛的博客)【图论】
题意:题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 poj2387 Description Bessie is out in the field and ...
- POJ 2240 Arbitrage (Bellman Ford判正环)
Arbitrage Time Limit: 1000MS Memory Limit: 65536K Total Submissions:27167 Accepted: 11440 Descri ...
- poj1860 兑换货币(bellman ford判断正环)
传送门:点击打开链接 题目大意:一个城市有n种货币,m个货币交换点,你有v的钱,每个交换点只能交换两种货币,(A换B或者B换A),每一次交换都有独特的汇率和手续费,问你存不存在一种换法使原来的钱更多. ...
- ACM/ICPC 之 Bellman Ford练习题(ZOJ1791(POJ1613))
这道题稍复杂一些,需要掌握字符串输入的处理+限制了可以行走的时间. ZOJ1791(POJ1613)-Cave Raider //限制行走时间的最短路 //POJ1613-ZOJ1791 //Time ...
随机推荐
- 4-1逻辑与运算符介绍 & 4-2逻辑或运算符介绍
后面括号内的(n++)不运算了. 4-2逻辑或运算符介绍
- ios NSFileManager创建目录、文件
NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *str1 = NSHomeDirectory(); _fi ...
- 阿里云的opensearch
官方文档:https://help.aliyun.com/document_detail/29104.html?spm=a2c4g.11186623.6.544.5215187be2TnnD
- 聊聊IT行业加班的问题
IT行业(包括互联网行业)是快速发展的行业,有时候一家公司同时可能要开发多个项目,并发进行,在公司开发人员相对固定的情况下,要想在指定的时间内完成项目谈何容易. 项目多.任务重.需求的不明确.技术难关 ...
- Memcached 查看帮助
进入到memcached目录, 输入命令: memcached -h 即可查看帮助 -p<num>要侦听的TCP端口号(默认值:11211) -u<num>udp监听端口号(默 ...
- SparkR安装
一.在虚拟机中安装R语言 1.下载R语言压缩包R-3.2.2.tar.gz,放在目标目录下 ★在此特别提醒,尽量安装3.2.?版本的R,更高版本的R容易出现依赖包安装不全的问题. # mv R-3.2 ...
- Mybatis源码解析(二)
根据上篇的代码跟踪mybatis已经ready好 SqlSessionFactory了,下面就是我们怎么去通过这个factory去获取sqlSession会话了,继续扒源码: mybatis-spri ...
- MyBatis逆向工程代码的生成以及使用详解(持续更新)
逆向工程简介什么是逆向工程: mybatis需要程序员自己编写sql语句,mybatis官方提供逆向工程,可以针对单表自动生成mybatis执行所需要的代码(mapper.java.ma ...
- iOS开发 - 多线程实现方案之Pthread篇
pthread基础 pthread是POSIX thread的简写,一套通用的多线程API,适用于Unix.Linux.Windows等系统,跨平台.可移植,使用难度大,C语言框架,线程生命周期由程序 ...
- Zynq7000开发系列-1(Vivado HLx 2016.4下载及安装)
一.下载 1.访问Xilinx官方网站:www.xilinx.com,注册帐号并完善个人信息 2.点击以下选项进入下载页面:Support -> Downloads & Licensin ...