最短路之SPFA算法
部分来自:http://blog.csdn.net/juststeps/article/details/8772755
求最短路径的算法有许多种,除了排序外,恐怕是OI界中解决同一类问题算法最多的了。最熟悉的无疑是Dijkstra,接着是Bellman-Ford,它们都可以求出由一个源点向其他各点的最短路径;如果我们想要求出每一对顶点之间的最短路径的话,还可以用Floyd-Warshall。
SPFA是这篇日志要写的一种算法,它的性能非常好,代码实现也并不复杂。特别是当图的规模大,用邻接矩阵存不下的时候,用SPFA则可以很方便地面对临接表。每个人都写过广搜,SPFA的实现和广搜非常相似。
如何求得最短路径的长度值?
首先说明,SPFA是一种单源最短路径算法,所以以下所说的“某点的最短路径长度”,指的是“某点到源点的最短路径长度”。
我们记源点为S,由源点到达点i的“当前最短路径”为D[i],开始时将所有D[i]初始化为无穷大,D[S]则初始化为0。算法所要做的,就是在运行过程中,不断尝试减小D[]数组的元素,最终将其中每一个元素减小到实际的最短路径。
过程中,我们要维护一个队列,开始时将源点置于队首,然后反复进行这样的操作,直到队列为空:
(1)从队首取出一个结点u,扫描所有由u结点可以一步到达的结点,具体的扫描过程,随存储方式的不同而不同;
(2)一旦发现有这样一个结点,记为v,满足D[v] > D[u] + w(u, v),则将D[v]的值减小,减小到和D[u] + w(u, v)相等。其中,w(u, v)为图中的边u-v的长度,由于u-v必相邻,所以这个长度一定已知(不然我们得到的也不叫一个完整的图);这种操作叫做松弛。
引用内容
松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对i,j进行松弛,就是判定是否d[j]>d[i]+w[i,j],如果该式成立则将d[j]减小到d[i]+w[i,j],否则不动。
(3)上一步中,我们认为我们“改进了”结点v的最短路径,结点v的当前路径长度D[v]相比于以前减小了一些,于是,与v相连的一些结点的路径长度可能会相应地减小。注意,是可能,而不是一定。但即使如此,我们仍然要将v加入到队列中等待处理,以保证这些结点的路径值在算法结束时被降至最优。当然,如果连接至v的边较多,算法运行中,结点v的路径长度可能会多次被改进,如果我们因此而将v加入队列多次,后续的工作无疑是冗余的。这样,就需要我们维护一个bool数组Inqueue[],来记录每一个结点是否已经在队列中。我们仅将尚未加入队列的点加入队列。
算法能否结束?
对于不存在负权回路的图来说,上述算法是一定会结束的。因为算法在反复优化各个最短路径长度,总有一个时刻会进入“无法再优化”的局面,此时一旦队列读空,算法就结束了。然而,如果图中存在一条权值为负的回路,就糟糕了,算法会在其上反复运行,通过“绕圈”来无休止地试图减小某些相关点的最短路径值。假如我们不能保证图中没有负权回路,一种“结束条件”是必要的。这种结束条件是什么呢?
思考Bellman-Ford算法,它是如何结束的?显然,最朴素的Bellman-Ford算法不管循环过程中发生了什么,一概要循环|V|-1遍才肯结束。凭直觉我们可以感到,SPFA算法“更聪明一些”,就是说我们可以猜测,假如在SPFA中,一个点进入队列——或者说一个点被处理——超过了|V|次,那么就可以断定图中存在负权回路了。
最短路径本身怎么输出?
在一幅图中,我们仅仅知道结点A到结点E的最短路径长度是73,有时候意义不大。这附图如果是地图的模型的话,在算出最短路径长度后,我们总要说明“怎么走”才算真正解决了问题。如何在计算过程中记录下来最短路径是怎么走的,并在最后将它输出呢?
Path[]数组,Path[i]表示从S到i的最短路径中,结点i之前的结点的编号。注意,是“之前”,不是“之后”。最短路径算法的核心思想成为“松弛”,原理是三角形不等式,方法是上文已经提及的。我们只需要在借助结点u对结点v进行松弛的同时,标记下Path[v] = u,记录的工作就完成了。
输出时可能会遇到一点难处,我们记的是每个点“前面的”点是什么,输出却要从最前面往最后面输,这不好办。其实很好办,见如下递归方法:
程序代码
void PrintPath(int k){
if( Path[k] ) PrintPath(Path[k]);
fout<<k<<' ';
}
SPFA的代码怎么写?
我写了邻接表和邻接矩阵两种,两者想像起来是那么的不同,算法的思路上实在区别不大,只是用不同方式诠释“扫描”的过程而已。
// 以上转载。感觉写的挺好,易懂。
好尴尬,spfa好懂,邻接表忘了。T_T.
强行补一下,自己理解的spfa。
首先这个算法是用来求一个图的源点到其它各点的最短路径。然后呢,这个图的存储形式可以选择邻接表和邻接矩阵(貌似是邻接表比临街矩阵还要快一点,具体的再补。)
然后就是求最短路径的过程了,dis[] 存储源点到所有点的初始距离,初始化为无穷大,只有源点是0。并推入队列。每次取队列的队首,对所有以这个点为起点的边做一次松弛,
直到队列为空为止,这期间可以记录每个顶点的前驱节点,可以通过判断当前顶点入队次数是否>=n,如果是说明有负环,不能寻找最短路。(至于为什么通过这个入队次数来判断是不是有负环:
最短路之SPFA算法的更多相关文章
- 2018/1/28 每日一学 单源最短路的SPFA算法以及其他三大最短路算法比较总结
刚刚AC的pj普及组第四题就是一种单源最短路. 我们知道当一个图存在负权边时像Dijkstra等算法便无法实现: 而Bellman-Ford算法的复杂度又过高O(V*E),SPFA算法便派上用场了. ...
- 最短路:spfa算法
板子补完计划绝赞继续中( 这篇博客就来写一写spfa(这我居然板子都打错了一次,我太弱啦!) 先来看一下定义:(引自http://blog.csdn.net/juststeps/article/det ...
- hdu 2544 最短路(SPFA算法)
本题链接:点击打开链接 本题大意: 首先输入一个n,m.代表有n个点.m条边.然后输入m条边,每条边输入两个点及边权.1为起点,n为终点.输入两个零表示结束. 解题思路: 本题能够使用SPFA算法来做 ...
- 851. spfa求最短路(spfa算法模板)
给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出impossible. 数据保证不存在负权回路. 输入格式 ...
- spfa 单源最短路究极算法
学习博客链接:SPFA 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm. SPFA算法是西南交通大学段凡丁于1994年发表的. 从名字我 ...
- 最短路径算法之四——SPFA算法
SPAF算法 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,该算法是西南交通大学段凡丁于1994年发表的. 它可以在O(kE)的时间复杂度内求出源点 ...
- poj 3662 Telephone Lines spfa算法灵活运用
意甲冠军: 到n节点无向图,它要求从一个线1至n路径.你可以让他们在k无条,的最大值.如今要求花费的最小值. 思路: 这道题能够首先想到二分枚举路径上的最大值,我认为用spfa更简洁一些.spfa的本 ...
- 最短路径:我的理解--SPFA算法
SPFA算法 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm. 最短路径快速算法-SPFA算法是西南交通大学段凡丁于1994年发表的. 适用范围:给定 ...
- hihocoder 1093 SPFA算法
题目链接:http://hihocoder.com/problemset/problem/1093 , 最短路的SPFA算法. 由于点的限制(10w),只能用邻接表.今天也学了一种邻接表的写法,感觉挺 ...
随机推荐
- github push error ---- recursion detected in die handler
错误提示如下: 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 New Bitmap Image.bmp Coun ...
- (2.13)Mysql之SQL基础——触发器
(2.13)Mysql之SQL基础——触发器 关键词:Mysql触发器 1.一般形式 -- 0.查看触发器[1]SHOW TRIGGERS;[2]SELECT * FROM `information_ ...
- centos shell脚本编程2 if 判断 case判断 shell脚本中的循环 for while shell中的函数 break continue test 命令 第三十六节课
centos shell脚本编程2 if 判断 case判断 shell脚本中的循环 for while shell中的函数 break continue test 命令 ...
- HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695 题目解析: Given 5 integers: a, b, c, d, k, you're to ...
- Mybatis入门和简单Demo
一.Mybatis的诞生 回顾下传统的数据库开发,JDBC和Hibernate是使用最普遍的技术,但这两种ORM框架都存在一定的局限性: JDBC:最原生的技术,简单易学,执行速度快,效率高,适合大数 ...
- 内存记号(Memory Trail)[自定义的名字] --调试方法
即使你把输出导向到文件中,运转记录花费的时间所带来的冲击,仍然足够改变程序的执行结果.如果要改善这种情况,我必须回到一个我所谓的“Memory Trails”(内存记号)的低阶技术中.为了使用 mem ...
- cocos代码研究(17)Widget子类RadioButtonGroup学习笔记
理论基础 RadioButtonGroup可以把指定的单选按钮组织起来, 形成一个组, 使它们彼此交互. 在一个RadioButtonGroup, 有且只有一个或者没有RadioButton可以处于被 ...
- python webdriver api-操作富文本框
操作富文本框-就是邮件正文部分,可以选字体啥的 第一种方式: 一般都是在iframe里,要切进去,一般是”html/body”,编辑之后,再切出来,然后再send_keys就完事儿 #encoding ...
- centos7修改hostname
[root@centos7 ~]$ hostnamectl set-hostname prd_web1 # 使用这个命令会立即生效且重启也生效 [root@centos7 ~]$ hostname # ...
- GPS数据解析
1.摘要 GPS模块使用串口通信,那么它的的数据处理本质上还是串口通信处理,只是GPS模块的输出的有其特定的格式,需要字符串处理逻辑来解析其含义.如何高效的处理从GPS模块接收到的数据帧,是GPS驱动 ...