Graph 图论
最短路(Bellman_Ford)
缺点:不能计算包含负环的图
前面我们已经学习了dijkstra算法来求最短路,但是dijkstra有一个很大的问题就是无法计算有负权值的图,这是因为dijkstra找到一个当前dist最小点的时候便将该点设置为已访问,便不再处理该点,这对边权为非负数的时候适用,因为该点的权值不会再被更新(dijkstra),然而如果一个图的边带有负权值,那么该算法便不再适用,因为即使是当前dist最小的点之后还有可能会被负值所更新,所以我们就不能像dijkstra那样将一个标记为已被访问就不在管他,我们不能对点进行处理了,所以我们换个方式而是对边进行处理,每一条边都对应了三个属性,一个是起点,一个是终点,一个是边权,所以我们很容易想到用一个结构体来存储这三种属性:
struct edge{
int a,b;
int cost;
};
edge map[600001];
然后我们就是对边进行处理了,一共我们有m条边,我们每次循环就要对这m条边进行遍历,确保我们每次更新dist值都能够考虑所有的边对其的影响,这样的循环最多进行 n-1 次(如果不存在从起点可达的负环,那么最短路不会经过同一个点两次,也就是说最多通过v-1条边,while循环醉倒执行n-1次),我们可以设置一个update来判断是否当前循环是否有更新dist值,如果进行中某次循环没有更新dist值,也就是说经过这次循环程序中的任何值都没有发生变化,所以就可以退出来了,就没有必要再做剩下的循环了(因为循环下去我们所需要的dist的值也不会再发生变化了)。
循环的主要内容是我们判断一下当前我们所知的这条边是否能对dist值进行更新,这个想法跟dijkstra一样,
if(dist[E.b] > dist[E.a] + E.cost)
=> dist[E.b] = dist[E.a] + E.cost;
完整代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const long long inf = 0x7f7f7f7f;
struct edge{
int a,b;
int cost;
};
edge map[600001];
int n,m;
int dist[100001];
void bellman_ford(int begin){
memset(dist,inf,sizeof(dist));
dist[begin] = 0;
bool update = true;
while(update){
update = false;
for(int i=0;i<m;i++){
edge E = map[i];
if(dist[E.b] > dist[E.a] + E.cost){
update = true;
dist[E.b] = dist[E.a] + E.cost;
}
}
}
}
int main(){
int begin;
cin >> n >> m;
cin >> begin;
for(int i=0;i<m;i++){
cin >> map[i].a >> map[i].b >> map[i].cost;
}
bellman_ford(begin);
for(int i=1;i<=n;i++){
if(dist[i]==inf) cout << 2147483647 << " ";
else cout << dist[i] << " ";
}
return 0;
}