Bellman-Ford的使用范围
1、单源
2、有负权重
3、有向
Bellman-Ford算法的描述
最短路径估计值g(v):当前求得的源节点s到节点v最短距离
最短路径d(v):源节点s带节点v的最短路径距离
Bellman-Ford算法通过对所有的边进行松弛操作来渐渐地降低从源节点s到每个节点v的最短路径估计值g(v),直到g(v)=d(v)。
Bellman-Ford算法的模板
class Edge//表示边,u为边的起点,v为边的终点,w为边的权重
{
int u;
int v;
int w;
}
int n;//节点的总个数
int m;//边的总条数
int s;//源节点
Edge e[];//e[i]:第i条边
int d[];//d[i]:s到i的最短路径长度
boolean bellman_ford()
{
d[s]=0;
boolean flag=false;
//*//对所有的边进行n-1次松弛
for(int i=1;i<n;i++)
{
flag=false;
//**//对所有的边进行松弛
for(int j=0;j<m;j++)
{
int u=e[j].u;
int v=e[j].v;
int w=e[j].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
flag=true;
}
}
if(!flag)//若flag为false说明本次没有对任何一条边进行松弛,那么以后的每一次都不会对任何一条边进行松弛,所以剩下的松弛操作也没必要了
{
break;
}
}
//**//
//***//判断是否有负环
for(int i=0;i<m;i++)
{
int u=e[i].u;
int v=e[i].v;
int w=e[i].w;
if(d[v]>d[u]+w)
{
return false;//有负环
}
}
return true;//没有负环
//***//
}
收敛性质:对于某些节点u,v,如果s~u~v是一条最短路径,并且在对边(u,v)进行松弛前的任意时间有g[u]=d[u],则在之后的所有时间有g[v]=d[v]。
证明Bellman-Ford的正确性
1、证明若不存在负环,则对所有边进行n-1次松弛之后,对于所有从s可以到达的节点v,有g[v]=d[v]
证明:设p=(s,v1,v2,…,vk)为从源节点到vk之间的任意一条最短路径。根据收敛性质,在进行第一次松弛后,g[v1]=d[v1],在进行第二次松弛后,g[v2]=d[v2],。。。,在进行第k次松弛后,g[vk]=d[vk]。由于p最多包含n-1条边,所以经过n-1次松弛后,对于所有从s可以到达的节点v,有g[v]=d[v]。
2、证明对所有边进行n-1次松弛之后,若有某条边(u,v),使得g(v)>g(u)+w(u,v),则存在负环
证明:我们可以断言:若没有负环,则对于任意一条边(u,v),有g(v)<=g(u)+w(u,v)。由于该命题成立,则该命题的逆否命题也成立。
所以可以推出:若有某条边使得g(v)>g(u)+w(u,v),则存在负环。得证
3、证明对所有边进行n-1次松弛之后,若对任意的边(u,v),都有g(v)<=g(u)+w(u,v),则不存在负环
证明:假设存在负环,设该环路为c=(v0,v1,v2,…,vk),v0=vk。
由于c未负环,所以有w(v0,v1)+w(v1,v2)+…+w(vk-1,vk)<0。
由于对于所有的边都有g(v)<=g(u)+w(u,v),
所以有g(vi)<=g(vi-1)+w(vi-1,vi),i=1,2,…,k,
将k个等式加起来,有g(v1)+g(v2)+…+g(vk)<=g(v0)+g(v1)+…+g(vk-1)+w(v0,v1)+w(v1,v2)+…+w(vk-1,vk),
由于v0=vk,
所以有g(v0)=g(vk)
所以有w(v0,v1)+w(v1,v2)+…+w(vk-1,vk)>=0.
这与w(v0,v1)+w(v1,v2)+…+w(vk-1,vk)<0相矛盾。所以命题正确