为了能够求解
含负权边的带权有向图
的单源最短路径问题,
Bellman(
贝尔曼
)
和
Ford(
福特
)
提出了
从源点逐次绕过其他顶点,以缩短到达终点的最短路径长度
的方法。
不能处理带负权边的无向图。图中不能包含权值总和为负值回路(负权值回路)。
不能处理带负权边的无向图。图中不能包含权值总和为负值回路(负权值回路)。
递推公式(求顶点u到源点v的最短路径):
dist 1 [u] = Edge[v][u]
dist k [u] =min{ distk-1 [u], min{ distk-1 [j] + Edge[j][u] } },j=0,1,…,n-1,j≠u
#define MAX_VER_NUM 10 //顶点个数最大值
#define MAX 1000000
int Edge[MAX_VER_NUM][MAX_VER_NUM]; //图的邻接矩阵
int vexnum; //顶点个数
int path[MAX_VER_NUM]; //path[i]是源到i的最短路径中的i的前面那个点
void BellmanFord(int v) //假定图的邻接矩阵和顶点个数已经读进来了
{
int i, k, u;
for(i=0; i<vexnum; i++)
{
dist[i]=Edge[v][i]; //对dist[ ]初始化
if( i!=v && dist[i]<MAX ) path[i] = v; //对path[ ]初始化
else path[i] = -1;
}
<pre name="code" class="cpp"> for(k=2; k<vexnum; k++) //从dist1[u]递推出dist2[u], …,distn-1[u]
{
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] )
{
dist[u]=dist[i]+Edge[i][u];
path[u]=i;
}
}
}
}
}
}
如果存在从源点可达的
负权值回路
,则最短路径不存在,因为可以重复走这个回路,使得路径无穷小。在 Bellman 算法中判断是否存在从源点可达的负权值回路的方法:
在求出distn-1[ ]之后,再对每条边<u,k>判断一下:加入这条边是否会使得顶点k的最短路径值再缩短,即判断:
dist[u]+w(u,k)<dist[k]
是否成立,如果成立,则说明存在从源点可达的负权值回路。
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
if (Edge[i][j]<MAX && dist[j]>dist[i]+Edge[i][j])
return 0;//存在从源点可达的负权值回路
}
}
return 1;
如果成立,则说明找到了一条经过了n条边的从 s到k的路径,且其比任何少于n条边的从s到k的路径都短。
一共n个顶点,路径却经过了n条边,则必有一个顶点m经过了至少两次。则m是一个回路的起点和终点。
走这个回路比不走这个回路路径更短,只能说明这个回路是负权回路。