当一张图中存在负权边时,Dijstra算法就无法算出正确的解,这个时候我们要用到Bellman-Ford算法。
注意:
1 当这张图存在负权环时是无法算出最短路径的,也可以用Bellman-Ford算法判断这张图是否有负权环。
2 Bellman-Ford算法一般处理的是有向图,因为如果是无向图,那么如果存在一条负权边,就会构成负权环了
复杂度: 邻接表表示图时O(VE),邻接矩阵表示时O(V^3)
算法原理:
如果一个图没有负权环,那么从一点到另一个点的最短路径最多经过所有的V个顶点,经过V-1条边
对一个点的一次松弛操作,就是找到经过这个点到与其相邻的点的另外一条路径,多一条边,而权值更小。那么我们只要对所有的点进行V-1次松弛操作,就可以求出从起点开始对任意一点的距离。如果还可以进行松弛操作,那么说明这个图有负权环。
(虽然说是对i点进行松弛操作,但实际上是对i直接相邻的点进行改变的)
算法步骤:
用dist数组存放从起点开始到第i个点的距离
v-1次循环,每次循环对所有点进行松弛操作。(当某一次循环任何一个点都没有成功松弛时,算法结束),因为如果本次循环没有点可以进行松弛,那么下一次的循环与这一次的将会一模一样,也不会有点可以进行松弛,直至n-1次循环后结束。
也就是说:只有上一次循环中松弛过的点才有可能参与下一次循环的松弛操作!!!(这个将是spfa提出的依据)
具体代码:
#include <iostream>
#include <vector>
#define maxn 10
#define INF 0x7fffffff
using namespace std;
int dist[maxn];
struct node{
int val;
int num;
node(int a = 0,int b = 0)
{
num = a;
val = b;
}
};
vector<node> g[maxn];
int main()
{
int n,m;
cin >> n >> m;
for( int i = 0 ; i < m ; i++ )
{
dist[i] = INF; //把每个点的花费都设为无穷大
int x,y,v;
cin >> x >> y >> v;
g[x].edge.push_back(node(y,v)); //有向图
}
dist[0] = 0; //假设我们要计算0到任意一点的最短路径
for( int i = 1 ; i < n ; i++ ) //v-1次循环
{
int flag = 0;
for( int j = 0 ; j < n ; j++ ) //遍历每一个节点对每个节点都进行松弛操作
{
for( int k = 0 ; k < g[j].edge.size() ; k++ ) //遍历与这个节点相邻的边
{
if( dist[j] + g[j].edge[k].val < dist[k] )
{
dist[k] = dist[j] + g[j].edge[k].val;
flag = 1;
}
}
}
if( !flag ) break; //如果一次都没有松弛,那么直接break
}
for( int i = 0 ; i < n ; i++ )
{
cout << dist[i] << endl;
}
return 0;
}
如果我们需要判断是否有负权环时,只要多进行一次循环,如果该循环还能进行松弛操作的话,就判断为有负权环。