Bellman-ford 算法
算法摘要
作用:单源有向图上求最短路
特点:能适应负权边的情况
思想:动态规划
时间复杂度: O(点数×变数)
原理:对所有的边进行n-1轮松弛操作,(n个顶点的图中,任意两点之间的路径最多包含n-1边)
注意事项:需要判断是否产生负权回路 (再对边进行一次内部循环,如果还有可以松弛的点,说明有负权回路)
c++代码
#include<iostream>
#include<vector>
#define ARRAY vector<int> //下标从0开始有效
#define INF 99999999
using namespace std;
struct Edge {
int from;
int to;
int val;
};
vector<Edge> edges;
int nodesNum, edgeNum, startPoint;
int main(){
//获取输入输入
scanf("%d %d %d", &nodesNum, &edgeNum, &startPoint);
for (int i = 0; i < edgeNum; i++) {
int from, to, val;
scanf("%d %d %d", &from, &to, &val);
edges.push_back({ from, to, val });
}
//计算最短路
ARRAY distance(nodesNum+1, INF); //从startPoint到各点的最短距离,下标从1开始有效
distance[startPoint] = 0;
for (int i = 0; i < nodesNum; i++) {
bool change = false; //标记是否发生松弛
for (int j = 0; j < edges.size(); j++){
Edge tmp = edges[j];
if (distance[tmp.to] > distance[tmp.from] + tmp.val){
distance[tmp.to] = distance[tmp.from] + tmp.val;
change = true;
}
}
if (!change) { //若没有松弛则结束算法
break;
}
}
//判断是否有负权回路
bool vaild = true;
for (int i = 0; i < edges.size() && vaild; i++){
Edge tmp = edges[i];
if (distance[tmp.to] > distance[tmp.from] + tmp.val){
vaild = false;
}
}
//输出结果
if (!vaild){
cout << "Fail\n";
return 0;
}
cout << "Success\n";
for (int i = 1; i <= nodesNum; i++){
printf("nodes: %d distance: %d \n", i, distance[i]);
}
cout << endl;
return 0;
}
后记
- 注意事项:注意图的类型,是有向还是无向图。说是无向图,则edges数组实际的长度是nodeNum的两倍,循环时不能用nodeNum.
- 易错点:注意数组下标关系,第一个节点在下标0还是下标
- 发现:无向图出现负权边则意味则产生负权回路,这时不能求最短路。所以负权边的情况只出现在单向图中。