转载:http://blog.youkuaiyun.com/niushuai666/article/details/6791765
概要
- Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。
- 所以就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个,时间复杂度O(VE)。
- spfa是在Bellman-Ford上的队列优化,时间复杂度O(kE) (k << V),但在最坏的情况下,有可能退化成O(VE)
适用条件和范围
- 单源最短路径(从源点s到其它所有顶点v)
- 有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图)
- 边权可正可负(如有负权回路输出错误提示)
- 差分约束系统
算法流程
- 给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,数组dis[i]记录从源点s到顶点i的路径长度,dis数组初始化为INF,dis[s]为0
执行以下操作至多n-1次,n为顶点数
对于每一条边e(u, v),如果dis[u] + w(u, v) < dis[v],则令dis[v] = dis[u]+w(u, v),w(u, v)为边e(u,v)的权值。 若上述操作没有对dis数组进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环
为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在dis[u] + w(u, v) < dis[v]的边,则图中存在负环路,即是说该图无法求出单源最短路径。否则数组dis[n]中记录的就是源点s到各顶点的最短路径长度。、
Bellman-Ford算法可以大致分为以下三个部分
1. 初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
2. 进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
3. 遍历途中所有的边(edge(u,v)),判断是否存在这样情况:d(v) > d (u) + w(u , v)
存在则返回false,表示途中存在从源点可达的权为负的回路。
代码
#include<iostream>
#include <vector>
using namespace std;
#define INF 0x3fffffff
const int maxn = 1010;
int n , m , s; //点,边,起点
struct Edge {
int from , to , v;
Edge() {}
Edge(int x , int y , int z) {from = x; to = y; v = z;}
};
vector<Edge> edges;
int dis[maxn], pre[maxn];
bool Bellman_Ford()
{
for(int i = 1; i <= n; i++) //初始化
dis[i] = (i == s ? 0 : INF);
for(int i = 1; i < n; i++) for(int j = 0; j < m; ++j) {
if(dis[edges[j].to] > dis[edges[j].from] + edges[j].v) //松弛(顺序一定不能反~)
{
dis[edges[j].to] = dis[edges[j].from] + edges[j].v;
pre[edges[j].to] = edges[j].from;
}
}
for(int i = 0; i < m; ++i) {
if(dis[edges[i].from] > dis[edges[i].to] + edges[i].v)
return false;
}
return true;
}
void print_path(int root) //打印最短路的路径(反向)
{
while(root != pre[root]) //前驱
{
cout << root << "-->";
root = pre[root];
}
if(root == pre[root]) cout << root << endl;
}
int main()
{
cin >> n >> m >> s;
pre[s] = s;
for(int i = 0; i < m; i++)
{
int from , to , v;
cin >> from >> to >> v;
edges.push_back(Edge(from , to , v));
}
if(Bellman_Ford()) {
for(int i = 1; i <= n; i++) //每个点最短路
{
cout << dis[i] << endl;
cout << "Path: ";
print_path(i);
}
}else cout << "have negative circle" << endl;
return 0;
}