Bellman-Ford算法(最短路径,解决负权边,检测负环回路)+队列优化

Bellman-Ford算法核心代码只有四行

for( k=1;k<=n-1;k++){
		for( i=1;i<=m;i++){
				if(dis[v[i]>dis[u[i]]+w[i]]){
					dis[v[i]]=dis[u[i]+w[i]];
				}
		}
	} 

外循环循环n-1次(n为顶点个数),内循环循环m次(m是边的个数),也即是枚举每一条边,dis数组和Dijkstra算法一样,用来记录源点到其余各顶点的最短路径。u,v,w三个数组用来记录边的信息。
例如第i条边存储在u[i]、v[i]、w[i]中,表示u[i]到顶点v[i]这条边(u[i]→v[i])权值为w[i].

if(dis[v[i]>dis[u[i]]+w[i]]){
					dis[v[i]]=dis[u[i]+w[i]];
				}

上面的代码是进行松弛操作,看能否通过u[i]→v[i]这条边,使得源点到顶点v[i]号顶点的距离变短。即源点到u[i]号顶点的距离dis[u[i]]加上u[i]→v[i]这条边(权值为w[i])的值是否会比原先的源点到v[i]号顶点的距离(dis[v[i]])要小,这个算法就是将所有的边都松弛一遍

一句话概括这个算法:对所有的边进行n-1次“松弛”

/*
测试数据 
5 5 1
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
*/
#include<cstdio>
#include<iostream>
using namespace std;
const int INF=9999999;
int dis[100],v[100],u[100],w[100];
int n,m,cnt,i,k;
int main(){
	cin>>n>>m>>cnt;//cnt到其他顶点的最短路径 
	for(i=1;i<=m;i++){
		cin>>u[i]>>v[i]>>w[i];
	} 
	//初始化dis数组
	for(i=1;i<=n;i++){
		dis[i]=INF;
	} 
	dis[cnt]=0;
	//Bellman-Ford算法核心语句 
	for( k=1;k<=n-1;k++){
		for( i=1;i<=m;i++){
			dis[v[i]]=min(dis[v[i]],dis[u[i]]+w[i]);
		}
	} 
	//输出结果
	for( i=1;i<=n;i++){
		printf("%d ",dis[i]);
	} 
	return 0;
} 

检测负权回路

此算法可以检测一个图是否存在负权回路,如果在进行n-1次松弛之后,仍然存在

if(dis[v[i]>dis[u[i]]+w[i]]){
					dis[v[i]]=dis[u[i]+w[i]];
				}

的情况,也就是说在进行n-1轮松弛之后,仍然可以继续松弛,那么此图必然存在负权回路

原因:一个图如果没有负权回路,那么最短路所包含的边最多有n-1条,即进行n-1轮松弛之后最短路不会再发生变化。如果在n-1轮松弛之后最短路仍然会发生变化,则改图必然存在负权回路
关键代码:

//Bellman-Ford核心语句 
	for(int k=1;k<=n-1;k++){
		for(int i=1;i<=m;i++){
			dis[v[i]]=min(dis[v[i]],dis[u[i]]+w[i]);	
		}
	} 
	bool flag=false;
	for(int i=1;i<=m;i++){
		if(dis[v[i]]>dis[u[i]]+w[i])flag=true;
	} 
	if(flag)printf("此图有负权回路\n");

进一步优化:

在实际操作中此算法经常在未达到n-1轮松弛就提前计算出最短路了,因此可以添加一个变量check用来标记数组dis在本轮中是否发生了变化,如果没有发生变化,则可以提前跳出循环:

#include<cstdio>
#include<iostream>
using namespace std;
const int INF = 2147483647;
int n,m,cnt,u[500100],v[500100],w[500100];
long long dis[10100];//dis数组需要开到long long 
int main(){
	//输入 
	cin>>n>>m>>cnt;
	for(int i=1;i<=m;i++){
		cin>>u[i]>>v[i]>>w[i];
	}
	//初始化 
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	}
	dis[cnt]=0;
	//Bellman-Ford核心语句 
	for(int k=1;k<=n-1;k++){
		bool check=false;//判断本轮松弛最短路有没有变化 
		for(int i=1;i<=m;i++){
			if(dis[v[i]]>dis[u[i]]+w[i]){//不能直接使用min函数 
				dis[v[i]]=dis[u[i]]+w[i];
				check=true;//dis数组发生更新,改变check的值	
			} 
		}
		if(check==false)break;//没有变化就没必要继续松弛了,提前结束 
	} 
	for(int i=1;i<=n;i++){
		printf("%d ",dis[i]);
	}
	return 0;
} 

队列优化

/*
5 5 1
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
*/ 
#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int INF = 2147483647;
int n,m,start,dis[10005];
struct node{
	int v,w;
	node(int vv,int ww){
		v=vv;
		w=ww;
	}
};
vector<node> ve[10005];
queue<int> que;
int main(){
	//输入 
	cin>>n>>m>>start;
	for(int i=0;i<m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		ve[x].push_back(node(y,z));//建立邻接表 
	}
	//初始化dis
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	} 
	dis[start]=0;
	//初始化队列
	que.push(start);
	//Bellman算法队列优化 
	while(!que.empty()){
		int u = que.front();
		for(int i=0;i<ve[u].size();i++){
			int v = ve[u][i].v;
			int w = ve[u][i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				que.push(v);
			}
		}
		que.pop();
	} 
	for(int i=1;i<=n;i++){
		printf("%d ",dis[i]);
	}
	return 0;
} 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值