Bellman-Ford算法与另一个非常著名的Dijkstra算法一样,用于求解单源点最短路径问题。Bellman-ford算法除了可求解边权均非负的问题外,还可以解决存在负权边的问题(意义是什么,好好思考),而Dijkstra算法只能处理边权非负的问题,因此 Bellman-Ford算法的适用面要广泛一些。但是,原始的Bellman-Ford算法时间复杂度为 O(VE),比Dijkstra算法的时间复杂度高,所以常常被众多的大学算法教科书所忽略,就连经典的《算法导论》也只介绍了基本的Bellman-Ford算法,在国内常见的基本信息学奥赛教材中也均未提及,因此该算法的知名度与被掌握度都不如Dijkstra算法。事实上,有多种形式的Bellman-Ford算法的优化实现。这些优化实现在时间效率上得到相当提升,例如近一两年被热捧的SPFA(Shortest-Path Faster Algoithm 更快的最短路径算法)算法的时间效率甚至由于Dijkstra算法,因此成为信息学奥赛选手经常讨论的话题。然而,限于资料匮乏,有关Bellman-Ford算法的诸多问题常常困扰奥赛选手。如:该算法值得掌握么?怎样用编程语言具体实现?有哪些优化?与SPFA算法有关系么?本文试图对Bellman-Ford算法做一个比较全面的介绍。给出几种实现程序,从理论和实测两方面分析他们的时间复杂度,供大家在备战省选和后续的noi时参考。
Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=(V,E),其源点为s,加权函数 w是 边集 E 的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。
(1) 初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
(2) 迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
(3) 检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
算法描述如下:
Bellman-Ford(G,w,s) :boolean //图G ,边集 函数 w ,s为源点
1 for each vertex v ∈ V(G) do //初始化 1阶段
2 d[v] ←+∞
3 d[s] ←0; //1阶段结束
4 for i=1 to |v|-1 do //2阶段开始,双重循环。
5 for each edge(u,v) ∈E(G) do //边集数组要用到,穷举每条边。
6 If d[v]> d[u]+ w(u,v) then //松弛判断
7 d[v]=d[u]+w(u,v) //松弛操作 2阶段结束
8 for each edge(u,v) ∈E(G) do
9 If d[v]> d[u]+ w(u,v) then
10 Exit false
11 Exit true
代码示例:
#include <iostream> #include <stdio.h> using namespace std; #define MAX 100000 #define N 1010 int nNode, nEdge, original; typedef struct Edge { int u, v; int cost; }Edge; Edge edge[N]; int d[N], pre[N]; void Relax(int u, int v, int w) { if(d[v] > d[u] + w ) { d[v] = d[u] + w; pre[v] = u; } } void Initialize_Single_source() { for( int i = 1; i <= nNode; i++) { d[i] = MAX; pre[i] = 0; } d[original] = 0; } bool Bellman_Ford() { Initialize_Single_source(); for( int i = 1; i < nNode; i++ ) { for( int j = 1; j <= nEdge; j++) { Relax(edge[j].u, edge[j].v, edge[j].cost); } } for( int j = 1; j <= nEdge; j++) { if( d[edge[j].u] > d[edge[j].v] + edge[j].cost) { return false; } return true; } } void Print_Path(int root) { if(0 != root) { Print_Path(pre[root]); printf("-->%d", root); } } int main(int argc, char* argv[]) { scanf("%d%d%d", &nNode, &nEdge, &original); for( int i = 1; i <= nEdge; i++ ) { scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost); } if (Bellman_Ford()) { for( int i = 1; i <= nNode; i++) { printf("\nnode%d: %d(km)\tPath:",i, d[i]); Print_Path(i); } printf("\n"); } else { printf("Have negative circle\n"); } return 0; }测试结果:
参考资料:
http://www.cppblog.com/infinity/archive/2011/10/20/66621.html
http://blog.csdn.net/niushuai666/article/details/6791765