Bellman-ford算法是求解连通带权图中单源最短路径的一种算法,它允许图中存在权值为负的边。同时它还能够判断出图中是否存在一个权值之和为负的回路。如果存在的话,图中就不存在最短路径(因为,假设存在最短路径的话,那么我们只要将这条最短路径沿着权值为负的环路在绕一圈,那么这条最短路径的权值就会减少了,所以不存在最短的路径,因为路径的最小值为负无穷),如果不存在的话,那么求出源节点到所有节点的最短路径。它的基本思想是,进行n-1迭代,分别计算出从源节点到每一个节点的最多有k条边的路径(k是迭代的次数)。刚开始的时候是到源节点最多只有一条边的所有路径(有的节点可能和源节点不相邻,此时路径权值是MAXINT),然后对每一条边进行判断,看这条边是否将边的尾节点的的最短路径缩短了,也就是判断源节点到头结点的最短路径加上边的权值是不是比源节点到尾节点的当前路径的权值短。这样一直进行n-1次迭代。就找出了从源节点到所有的节点的最多有n-1条边的最短路径。最后再判断一次,如果此时还有更短的路径产生,那么图中就存在权值为负的环路。具体实现代码如下:
/**
这边实现Bellman-ford算法, bellman-ford 算法就是不断的使用松弛技术
图使用邻接表表示,算法复杂度为:O(VE)
**/
#include <iostream>
using namespace std;
#define MAX_NODES 100000 //最大节点数
#define MAX_EDGES 100000 //最多的边数
#define MAX_DIST 100000 //最长的可能路径
class Edge
{
//三个变量,头尾节点和边的权值
public:
int head ,tail ;
double cost ;
Edge (int head,int tail ,double cost):head(head),tail(tail),cost(cost){}
Edge (){}
};
Edge* edges [MAX_EDGES] ; //用来存放边
double mindis[MAX_NODES]; //用来存放每个节点的最短路径
int nodes; //实际的节点数
int edgenum; //图中的边数
int s ; //源节点
//这边的默认的输入格式是节点数,边数,源节点,然后是所有的边(头尾节点,权值)
void init()
{
cin >> nodes >> edgenum >> s ;
for(int i = 0 ;i < nodes ;i ++)
mindis [i] = MAX_DIST;
mindis [s]= 0;
for(int i = 0 ;i < edgenum ;i++)
{
int head,tail ;
double cost ;
cin >> head >> tail >> cost;
edges[i] = new Edge(head,tail,cost);
if(head == s)
mindis [tail] = cost;
}
for(int i = 0 ;i < nodes ;i ++)
cout << mindis[i] <<" " ;
cout << endl <<endl;
}
bool bellman_ford()
{
bool changes = false;
for(int i = 0 ; i < nodes ; i++)
{
changes = false ;
for(int j = 0 ; j < edgenum ; j ++)
{
int head = edges[j]->head; //这边是j啊!!!!!!!!!!!!!!!
int tail = edges[j]->tail;
double cost = edges[j]->cost;
// cout << (mindis[head] + cost) << " " << head << "--head " << mindis[tail] << endl;
if((mindis[head] + cost )< mindis[tail])
{
mindis[tail] = mindis[head] + cost ;
changes = true ;
}
}
}
return changes;
}
/**
测试数据
5
7
0
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 60
**/
int main()
{
init();
if(!bellman_ford())
for(int i = 0 ;i < nodes; i++)
cout << mindis [i] << endl;
return 0;
}