[算法导论读书笔记]Bellman-Ford算法(单源最短路径)

    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算法思想

    Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=(V,E),其源点为s,加权函数 w是 边集 E 的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。

Bellman-Ford算法流程分为三个阶段:

(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   //,边集 函数 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;
}
测试结果:

[算法导论读书笔记]Bellman-Ford算法(单源最短路径)_第1张图片

参考资料:

http://www.cppblog.com/infinity/archive/2011/10/20/66621.html

http://blog.csdn.net/niushuai666/article/details/6791765


你可能感兴趣的:(编程,优化,算法,struct,读书,Path)