B e l l m a n _ F o r d Bellman\_Ford Bellman_Ford 算法:
memset(dist, 0x3f, sizeof(dist)) 和 dist[1] = 0
源点距离为0,其余点无穷大。- 两层 f o r for for :第一层循环 n − 1 n - 1 n−1 次( n n n 指最多经过边的数量,因为 1 1 1 到 n n n 的最短路最多经过 n − 1 n-1 n−1 条边),第二层循环 m m m 次(边的数量),更新最短距离。第二层循环有很多不被更新的点,因为父节点没被更新,其子节点也不会被更新。因此可以优化出 S P F A SPFA SPFA 算法。
- 时间复杂度: O ( n m ) O(nm) O(nm)
- 与DIjkstra的区别:Dijkstra每次更新的是一个最短距离的点,Bellman_Ford每次更新 i + 1 i+1 i+1 条边以内的点( i i i 是第一层循环变量,初值是0)。
使用场景:
- 判断负环:如果 i = n i =n i=n 时还可以进行更新,一定存在负环。因为若没有负环,此时所有的点的距离都被确定了。
- 求单源最短路(可以有负权边或边数限制)
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510, M = 10010;
// Edge:边的方向及距离。
struct Edge
{
int a, b, w;
}edges[M]; // 根据边数大小开数组
int dist[N], last[N]; //dist[]:记录距离,last[]:记录上一次的距离
int n, m, k; // n:点数 m:边数 k:最多经过边数
void bellman_ford()
{
// 初始化距离
memset(dist, 0x3f, sizeof(dist));
dist[1] = 0;
// k:更新1号点k条边以内所有点的最短距离。
// 若循环n - 1次,可算出1到n的最短路。
for (int i = 0; i < k; i ++ )
{
// 这里需要注意使用上一次的dist数组,防止使用了不该更新点
memcpy(last, dist, sizeof(dist));
for (int j = 0; j < m; j ++ )
{
auto e = edges[j];
// 上一次 a -> b的距离来更新现在的dist[b]
dist[e.b] = min(dist[e.b], last[e.a] + e.w);
}
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m >> k;
for (int i = 0; i < m; i ++ )
{
int a, b, w;
cin >> a >> b >> w;
edges[i] = {a, b, w};
}
bellman_ford();
// 由于存在负权边,但有最多边数限制,dist[n]不会是-INF,但可能比INF要小
if (dist[n] > 0x3f3f3f3f / 2) cout << "impossible" << endl;
else cout << dist[n] << endl;
return 0;
}
参考资料:
https://blog.csdn.net/dshf_1/article/details/108144085
https://zhuanlan.zhihu.com/p/72185593
https://www.cnblogs.com/nonames/p/12657562.html