Bellman-Ford算法

最短路径问题

从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径。

解决方法:

  • Floyd算法
  • Bellman-Ford算法
  • SPFA算法
  • Dijkstra算法

Bellman-Ford算法

算法特点:用于解决单源最短路径问题,并且具有判断负圈的功能。该算法只对相邻节点进行计算,可以避免Floyd那种大撒网式的无效计算,相对提高了效率(因为对比Dijkstra仍然略显笨重)。

算法思路:初始时,设所有节点到原点的距离为inf即无限大。

  • 第一轮:从原点节点开始,判断它的相邻节点到原点的距离,如果相邻节点到原点的距离小于inf,则把原来的inf更新为较短距离。原点的直接邻点肯定能够更新距离,而这些点的相邻节点也可以通过他们更新到原点的距离。如此下去,就更新所有节点到原点的距离。此处节点通过相邻节点更新距离成为松弛,因为在正权图中,每个节点的松弛次数必然是有限的,所以如果某个节点能够一直松弛下去,说明该节点到原点之间存在负圈。
for(int i=0; i dis[e[i].v] + e[i].w){
        dis[e[i].u] = dis[e[i].v] + e[i].w;
        pre[e[i].u] = e[i].v;//如果e[i].u经过e[i].v点到原点距离更短,则更新距离,并记录路径 
    } 
}
  • 第二轮:重复第一轮的操作,无非需要增加节点到原点的距离与节点通过相邻节点再到原点的距离的比较,比较之后取其中的较小值即可。
  • 第n轮:……

每一轮至少有一个新节点得到了到原点的最短路径,所以最多只需要n轮操作就能完成n个节点的最短路径。据次分析,Bellman-Ford算法的复杂度是O(nm)。同时为了实现算法判断负圈的功能,要增加一个一个整型用以记录执行到第几轮循环,如果超过n圈仍然能够松弛,则证明存在负圈。

if(k > n){printf("有负圈");return ;}    //有负圈,停止

当然,我们没有必要真的执行n轮循环,只要再增加一个布尔型(整型也可)用来记录此轮循环是否还有节点松弛,如果有则可以继续执行下轮循环;如果没有,说所有节点都已取得最短路径,不能再松弛,则跳出循环即可。完整代码如下:

#include
#include
#include
#include
using namespace std;
const int num = 1001;
const int inf = 0x3f3f3f3f;

int n, m;
int dis[num];//到原点距离 
int pre[num];//记录前驱节点 
struct edge{
	int u, v, w;
	edge(int a, int b, int c){u=a;v=b;w=c;}
};
vectore;

void print_path(int u, int v){//输出路径 
	if(u==v){	printf("%d", u);return;}
	print_path(u, pre[v]);
	printf("->%d", v);
}

void bellman(){
	int s = 1;//定义起点为1 
	for(int i=0; i<=n; i++)
		dis[i] = inf;
	dis[s] = 0;
	int k=0, flag=1;//k轮循环,flag是否有更新
	
	while(flag){
		k++;
		flag = 0;
		if(k > n){printf("有负圈\n");return ;}
		for(int i=0; i<2*m; i++){
			if(dis[e[i].u] > dis[e[i].v] + e[i].w){
				dis[e[i].u] = dis[e[i].v] + e[i].w;
				flag = 1;
				pre[e[i].u] = e[i].v;
			}
		}
	} 
}

int main(){
	while(~scanf("%d%d", &n, &m) &&n &&m){
            e.clear();
		for(int i=0; i

(这里我用到了vectore,主要是方便我在读入时的加边,当然定义普通的结构体数组同样能够完成Bellman的函数)

 

你可能感兴趣的:(#,最短路)