算法 | Bellman-Ford 算法
解决赋权图的单源最短路径问题:Bellman-Ford (贝尔曼-福特) 算法
- 能解决负边
- 能解决负环
Bellman-Ford 算法
复杂度
时间复杂度:
(
分析
Dijkstra 算法仅对确定点的出边做松弛操作,Bellman-Ford 算法更为暴力,每次迭代对所有的边做松弛操作,共进行
伪代码如下:
bellman_ford(G, dist[])
初始化: dist[1~V]=+INF, dist[1]=0
for (循环V-1次)
for (t : 所有边的集合)
if (以t为中介点到s的距离更短)
更新dist[s];
迭代次数
上述进行
迭代次数实际上也有意义,若进行
负权回路
当图中出现负权回路时,有可能不存在最短路,示例如下:
若要判断是否有负权回路,可以在
无穷大判断
由于在代码实现中我们往往使用一个较大的数作为
松弛后,后者的值变成了
串联问题
在代码实现中,若每次迭代使用的就是本次迭代的值,很有可能产生串联问题,即本次迭代中前面计算出的值被后面的计算使用,这样会产生错误。为了限制本次迭代使用的值一定为上次迭代的值,我们需要备份保存一遍上次的值,对应下面代码中 memcpy 语句。
代码实现
由于该算法只需遍历所有的边即可,所以存图方法比较简单粗暴,直接用一个结构体数组存下来所有边即可。
const int MAXN = 510, MAXM = 1e4 + 10, INF = 0x3f3f3f3f;
int v, e, k; // v顶点数 e边数 k经过边数限制
int dist[MAXN], backup[MAXN]; // dist最短距离 backup备份上一次迭代
struct Edge
{
int a, b, w; // a起点 b终点 w权值
} edges[MAXM]; // 结构图数组存边
int bellman_ford()
{
memset(dist, 0x3f, sizeof(dist));
dist[1] = 0;
for (int i = 0; i < k; i++)
{
memcpy(backup, dist, sizeof(dist));
for (int j = 0; j < e; j++)
{
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
dist[b] = min(dist[b], backup[a] + w);
}
}
return dist[v] > INF / 2 ? INF : dist[v];
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi