Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。该算法由美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明。
适用条件&范围
1.单源最短路径(从源点s到其它所有顶点v); 2.有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图); 3.边权可正可负(如有负权回路输出错误提示); 4.差分约束系统;
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 //图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 <stdio.h>
typedef struct Edge
{
int a;
int b;
int weight;
} Edge;
#define MAX 0x3f3f3f3f
#define MAX_EDGE_NUM 50
#define N 1000
Edge edge[MAX_EDGE_NUM];
int dist[N],pre[N];
bool BellmanFord(int v,Edge edge[],int vertexnum,int edgenum,int dist[],int pre[])
{
/* negative cycle detected return false */
int i,j;
for(i=0;i<vertexnum;i++)
{
if (i != v)
dist[i] = MAX;
else
dist[i] = 0;
pre[i] = v;
}
for(i=1;i<=vertexnum;i++)
for(j=0;j<edgenum;j++)
{
/* 松弛n次,每次按照边的相同顺序;第n(vertexnum)次检测负环*/
if(dist[edge[j].b] > dist[edge[j].a] + edge[j].weight){
if (i == vertexnum)
return false;
dist[edge[j].b] =
dist[edge[j].a] + edge[j].weight;
pre[edge[j].b] = edge[j].a;
}
}
return true;
}
void print_path(int v,int pre[],int n)
{
int i,j;
for(i=0;i<n;i++)
{
j = i;
printf("%d<-",j);
while(pre[j] != v)
{
printf("%d<-",pre[j]);
j = pre[j];
};
printf("%d\n",v);
}
}
int main()
{
int a,b,weight,vertexnum,edgenum;
scanf("%d%d",&vertexnum,&edgenum);
for(int i=0;i<edgenum;i++)
{
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].weight);
}
BellmanFord(0,edge,vertexnum,edgenum,dist,pre);
for(int i=0;i<vertexnum;i++)
printf("%d ",dist[i]);
printf("\n");
for(int i=0;i<vertexnum;i++)
printf("%-3d",pre[i]);
printf("\n");
print_path(0,pre,5);
}
输入:
5 6
0 4 2
4 3 4
4 1 5
1 3 -2
2 1 -3
3 2 6
输出:
0 7 11 5 2
0 4 3 1 0
0<-0
1<-4<-0
2<-3<-1<-4<-0
3<-1<-4<-0
4<-0
REF:
1,算法技术手册George T.Heineman
2,http://www.cnblogs.com/Jason-Damon/archive/2012/04/21/2460850.html
3,http://blog.csdn.net/niushuai666/article/details/6791765