概述
单源最短路径是要解决这样一类问题:给定一个图
G=(V,E)
,找到从给定源结点
s∈V
到每个结点
v∈V
的最短路径。其中,从结点
u
到结点
v
v的最短路径权重
δ(u,v)
定义为:
最短路径具有最优子结构的性质:两个顶点之间的一条最短路径包含着其他的最短路径。
Bellman-Ford算法可以处理包含负权重边的图,只要没有可以从源结点到达的权重为负值的环路。如果图中存在一条权重为负值的环路,此算法也可以检测出来。
Dijkstra算法要求所有边的权重为非负值。
一条最短路径不能包含权重为负值的环路,也不能包含权重为正值的环路,因为只要将环路从路径上删除就可以得到一条权重更小的路径,实际上,最短路径上也不会包含权重0的环路。因此,一条最短路径最多包含 V−1 条边。
Bellman-Ford 算法
Bellman-Ford算法通过对边进行松弛操作来渐进地改善源结点s到每个结点v的最短路径。对于每个结点
v
,维持一个属性
v.d
,用来记录从源结点
s
s到结点
v
的最短路径权重的上界,松弛操作描述为:
其实,这些最短路算法的不同之处就在于对每条边的进行松弛的次数和松弛边的次序。Dijkstra对每条边仅松弛一次,Bellman-Ford对每条边(最多)松弛 V−1 次 。
之所以说是最多,是因为如果在某次循环过程中,所有的边松弛过后都没能改变当前源点到其余各点的最短路径,那么算法可以提前结束。
# include <iostream>
# include <cstdio>
# include <vector>
# include <algorithm>
using namespace std;
const int MAX_N = 1000;
const int MAX_E = 1000;
const int INF = 1 << 30;
//定义边的数据结构
struct edge{
int from, to, cost;
edge(int a = 0, int b = 0, int c = 1):from(a), to(b), cost(c){}
};
edge es[MAX_E]; //边
int d[MAX_N]; //源点到其余各点的最短距离
int V, E; //顶点数,边数
bool Bellman_Ford(int s)
{
for(int i = 0; i < V; ++i) //初始化
d[i] = INF;
d[s] = 0;
for(int k = 0; k < V - 1; ++k) //最多 V - 1 次循环
{
bool update = false;
for(int i = 0; i < E; ++i)
{
edge e = es[i];
if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost) //松弛操作
{
d[e.to] = d[e.from] + e.cost;
update = true;
}
}
if(!update) break;
}
//检测是否存在权重为负值的环路
for(int i = 0; i < E; ++i)
{
edge e = es[i];
if(d[e.to] > d[e.from] + e.cost)
return false;
}
return true;
}
int main()
{
int a, b, cost;
while(cin >> V >> E)
{
fill(d, d + MAX_N, 0);
fill(es, es + MAX_E, 0);
for(int i = 0; i < E; ++i)
{
cin >> a >> b >> cost;
es[i] = edge(a, b, cost);
}
if(Bellman_Ford(0))
{
for(int i = 0; i < V; ++i)
{
if(i) cout << " ";
cout << d[i];
}
cout << endl;
}
else
{
cout << "There is an negative cycle!" << endl;
}
}
return 0;
}