一、算法介绍:
为了能够求解边上带有负值的单源最短路径问题,Bellman(贝尔曼)和Ford(福特)提出了从源点逐次绕过其他顶点,以缩短到达终点的最短路径长度的方法。
Bellman-ford算法是求解连通带权图中单源最短路径的一种常用算法,它允许图中存在权值为负的边。 同时它还能够判断出图中是否存在一个权值之和为负的回路。如果存在的话,图中就不存在最短路径(因为,假设存在最短路径的话,那么我们只要将这条最短路径沿着权值为负的环路再绕一圈,那么这条最短路径的权值就会减少了,所以不存在最短的路径,因为路径的最小值为负无穷),如果不存在的话,那么求出源点到所有节点的最短路径。
二、Bellman-Ford算法:
初始d[起点]=0,其他点d值为∞。从1开始迭代路径长度上线为k,每次考虑图中的每条边(u,v)并检查是否有dv>du+w(u,v).若有,则更新dv的值。写成伪代码就是:
for k:=1 to n-1 do
for 每条边(u,v) do
if (d[u]<∞) and (d[v]>d[u]+w(u,v)) then d[v]:=d[u]+w(u,v)
松弛操作:每次比较dv和du+w(u,v)及更新dv叫做一次松弛操作。
时间复杂度:算法时间复杂度为O(nm)。
三、代码:
static final int MAX=99999;
int Edge[][]; //图的邻接矩阵
int vexnum; //顶点个数
void BellmanFord(int v)//v为起点
{//假定图的邻接矩阵和顶点个数已经读进来了
int i, k, u;
for(i=0; i< vexnum; i++)
{
dist[i]=Edge[v][i]; //对dist[]初始化
if( i!=v && dis[i]< MAX )
path[i] = v;//对path[ ]初始化
else
path[i] = -1;
}
//如果dist[]各元素的初值为MAX,则循环应该n-1次,即k的初值应该改成1。
for(k=2; k< vexnum; k++)
{ //从dist(1)[u]递推出dist(2)[u], …,dist(n-1)[u]
bool flag=false;
for(u=0; u< vexnum; u++)
{//修改每个顶点的dist[u]和path[u]
if( u != v )
{
for(i=0; i< vexnum; i++)
{//考虑其他每个顶点
if( Edge[i][u]< MAX &&dist[u]>dist[i]+Edge[i][u] )
{
flag=true;
dist[u]=dist[i]+Edge[i][u];
path[u]=i;
}
}
}
}
if (!flag)
break;//当迭代无任何值改变时,可直接退出循环
}
}
四、有无负权回路的判断
在bellman-ford算法求出结果之后,继续检查回路是否存在
for(i=0; i< vexnum; i++) { if( dist[u]>dist[i]+Edge[i][u] )
{
//存在回路
}
}
原理:若再次进行松弛操作的时候依然进入if判断语句,则一定存在负权回路,因为负权回路中每次
dist[u]>dist[i]+Edge[i][u]
总是成立
五、例题
http://poj.org/problem?id=1860
货币兑换,单元点最短路径
题目大意:
给定N种货币,某些货币之间可以相互兑换,现在给定一些兑换规则,问能否从某一种货币开始兑换,经过一些中间货币之后,最后兑换回这种货币,并且得到的钱比之前的多。
思路:
由于所有点之间都是双向联通,所以可以以初始货币为起点和终点,无需担心是否存在通路问题,只需要求出是否存在正权环路即可。因为存在正权回路时货币可以通过无限循环回路增加货币总量。
利用bellman-ford算法中,处理一遍之后进行回路判断,存在dis[edge[j][0]] – cost[j][1])*cost[j][0]>dis[edge[j][1]]则说明有正权回路。
六、Talk is cheap,show me the code(例题代码)
#include <iostream>
using namespace std;
const int Vertex = 110;
const int Edge = 210;
int cnt = 0;
int edge[Edge][2];
double cost[Edge][2];
double dis[Vertex];
bool Bellman(int N, int S, double V)
{
int i,j;
for (i = 1; i <= N; i++)
dis[i] = 0;
dis[S] = V;
for (i = 1; i < N; i++)
{
bool flag = false;
for (j = 0; j < cnt; j++)
{
if ((dis[edge[j][0]] - cost[j][1])*cost[j][0]>dis[edge[j][1]])
{
dis[edge[j][1]] = (dis[edge[j][0]] - cost[j][1])*cost[j][0];
flag = true;
}
}
if (!flag)
return false;
}
for (j = 0; j < cnt; j++)
{
if ((dis[edge[j][0]] - cost[j][1])*cost[j][0]>dis[edge[j][1]])
return true;
}
return false;
}
int main(void)
{
int N, M, S;
double V;
cin >> N >> M >> S >> V;
int i, j;
for (i = 0; i < M; i++)
{
int a, b;
double rab, cab, rba, cba;
cin >> a >> b >> rab >> cab >> rba >> cba;
edge[cnt][0] = a;
edge[cnt][1] = b;
cost[cnt][0] = rab;
cost[cnt][1] = cab;
cnt++;
edge[cnt][0] = b;
edge[cnt][1] = a;
cost[cnt][0] = rba;
cost[cnt][1] = cba;
cnt++;
}
if (Bellman(N,S,V))
cout << "YES" << endl;
else
cout << "NO" << endl;
return 0;
}