求最短路径的算法有许多种,除了排序外,恐怕是ACM界中解决同一类问题算法最多的了。最熟悉的无疑是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算法的更多相关文章

  1. 最短路径--SPFA 算法

    适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了. 我们约定有向加权图G不存在负权回路,即最短路径一 ...

  2. 最短路径——SPFA算法

    一.前提引入 我们学过了Bellman-Ford算法,现在又要提出这个SPFA算法,为什么呢? 考虑一个随机图(点和边随机生成),除了已确定最短路的顶点与尚未确定最短路的顶点之间的边,其它的边所做的都 ...

  3. 图的最短路径-----------SPFA算法详解(TjuOj2831_Wormholes)

    这次整理了一下SPFA算法,首先相比Dijkstra算法,SPFA可以处理带有负权变的图.(个人认为原因是SPFA在进行松弛操作时可以对某一条边重复进行松弛,如果存在负权边,在多次松弛某边时可以更新该 ...

  4. 最短路径SPFA算法(邻接表存法)

    queue <int> Q; void SPFA (int s) { int i, v; for(int i=0; i<=n; i++) dist[i]=INF; //初始化每点i到 ...

  5. 洛谷P3371单源最短路径SPFA算法

    SPFA同样是一种基于贪心的算法,看过之前一篇blog的读者应该可以发现,SPFA和堆优化版的Dijkstra如此的相似,没错,但SPFA有一优点是Dijkstra没有的,就是它可以处理负边的情况. ...

  6. 最短路径问题的Dijkstra和SPFA算法总结

    Dijkstra算法: 解决带非负权重图的单元最短路径问题.时间复杂度为O(V*V+E) 算法精髓:维持一组节点集合S,从源节点到该集合中的点的最短路径已被找到,算法重复从剩余的节点集V-S中选择最短 ...

  7. Bellman-Ford & SPFA 算法——求解单源点最短路径问题

    Bellman-Ford算法与另一个非常著名的Dijkstra算法一样,用于求解单源点最短路径问题.Bellman-ford算法除了可求解边权均非负的问题外,还可以解决存在负权边的问题(意义是什么,好 ...

  8. 最短路径算法之四——SPFA算法

    SPAF算法 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,该算法是西南交通大学段凡丁于1994年发表的. 它可以在O(kE)的时间复杂度内求出源点 ...

  9. 数据结构与算法--最短路径之Bellman算法、SPFA算法

    数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...

随机推荐

  1. POJ 1430

    上面的估计是题解吧....呃,如果真要用到公式的话,确实没听过.... #include <iostream> #include <cstdio> #include <a ...

  2. Webstorm快捷键整理

    Webstorm快捷键整理 F2/Shift F2  下一个/上一个高亮错误 Ctrl+Shift+BackSpace 回到刚刚编辑的地方 Alt+Insert 新建文件,还有其他功能 Ctrl+D ...

  3. POJ 1765 November Rain

    题目大意: 有一些屋顶,相当于一些线段(不想交). 问每一条线段能够接到多少水,相对较低的屋顶能够接到高屋顶留下的水(如题图所看到的).因为y1!=y2,所以保证屋顶是斜的. 解题思路: 扫描线,由于 ...

  4. C++编写绚丽的界面

    近期项目特别的操蛋,要用C++写出各种变态界面,今晚上赶工总算有了一点小的收货. 因为没有时间去写博文 ,等项目期完了  准备 写一系列 怎样在C++/win32/mfc开发高质量  可扩展界面组建 ...

  5. MS UI Automation简介

    转自:http://blog.csdn.net/ffeiffei/article/details/6637418 MS UI Automation(Microsoft User Interface A ...

  6. node09---中间件

    如果我的的get.post回调函数中,没有next参数,那么就匹配上第一个路由,就不会往下匹配了. 如果想往下匹配的话,那么需要写next() 1app.get("/",funct ...

  7. iOS CoreData介绍和使用(以及一些注意事项)

    iOS CoreData介绍和使用(以及一些注意事项) 最近花了一点时间整理了一下CoreData,对于经常使用SQLite的我来说,用这个真的有点用不惯,个人觉得实在是没发现什么亮点,不喜勿喷啊.不 ...

  8. zzulioj--1711--漂洋过海来看你(dfs+vector)

    1711: 漂洋过海来看你 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 89  Solved: 33 SubmitStatusWeb Board D ...

  9. WIN32常用

    //1.设置窗口大小 MoveWindow(hWnd, , , + , + , FALSE); //放在InitInstance用于设置位置与窗口大小 //传递句柄就可以绘图 //2.双缓冲绘图模式 ...

  10. NOIP 2012 T2 国王游戏 (贪心+高精)

    思路: 呃呃网上那么多题解写得都不错-.. 就是高精 巨坑... 这里展出的是任氏高精(纯自己yy滴) //By SiriusRen #include <cstdio> #include ...