算法作用
计算单源最短路径, 给定图和起点后, 计算这个点到其他点的最短距离, 如果起点和终点都给定了也属于这类问题
比如 :
给定图如下
求1 到 其他各点的最短距离
可以计算出为 :
顶点 | 最短距离 |
---|---|
1 | 0 |
2 | 3 |
3 | 4 |
4 | 11 |
5 | 22 |
(口算的, 大概没算错吧)
算法复杂度
O(N * M) N是顶点个数, M是边数
代码实现
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 10;
const int M = 20;
const int INF = 0x3f3f3f3f;
struct Edge {
int from, to, cost;
};
int dis[N], n, m;
Edge edge[M];
void bellmanFord(int s) {
for(int i = 0; i < n; ++i)
dis[i] = INF;
dis[s] = 0;
for(int i = 1; i < n; ++i) {
for(int j = 0; j < m; ++j) {
Edge e = edge[j];
dis[e.to] = min(dis[e.to], dis[e.from] + e.cost);
}
}
}
int cnt = 0;
void readEdge(int x, int y, int z) {
--x, --y;
edge[cnt].from = x;
edge[cnt].to = y;
edge[cnt].cost = z;
cnt++;
}
int main() {
readEdge(1, 2, 3);
readEdge(3, 1, 4);
readEdge(2, 4, 8);
readEdge(4, 3, 14);
readEdge(4, 5, 11);
n = 5, m = cnt;
bellmanFord(0);
for(int i = 0; i < n; ++i)
cout << dis[i] << endl;
}
算法拓展
可以用来计算负环(一个环他的所有边的权值和小于0)
有时间再更新吧, 需要用到队列, 而且要换一种姿势
算法证明
假如我求s到其他点的最短路, 不妨假设这些点为t(总共有n个顶点)
s到t最多有n-1个中间点, 假设为k[1], k[2], k[n-1],
第一次循环 找到s –> k[1]这条边, 并更新s–>k[1]的距离
第二次循环 找到k[1]–>k[2]这条边, 并更新s–>k[2]的距离
……
第n-1次循环 找到k[n-1]到t这条边, 并更新s–>t的距离
如何知道s–> k[1] , k[1] –> k[2]这些边呢? 简单, 把所有边都拿出来, 都拿去试试就行了, 在min取最小值的操作中就找到了
PS : 这种操作叫松弛操作
至于判断负环的操作….留坑
代码具体实现思路
for(int i = 1; i < n; ++i) {
for(int j = 0; j < m; ++j) {
Edge e = edge[j];
dis[e.to] = min(dis[e.to], dis[e.from] + e.cost);
}
}
核心就这两个for
第一个for进行n-1次松弛
第二个for进行所有边的遍历
复杂度证明
明显的两个for的复杂度, 所以O(n * m)