算法竞赛备赛——【图论】求最短路径——Bellman-Ford & SPFA

Bellman-Ford

存图:边集数组/二维vector/链式前向星

最多松弛n-1轮

每松弛一轮至少可以确定一个点的最短距离,每一轮至少有一条边被松弛掉

不能求负权回路,但可以检验负环

模板

C++ 复制代码
#include<bits/stdc++.h>
using namespace std;
#define inf 1001 

//边集数组----->带权有向图为例
int n,m,s; //n<=100  -1000<边权<1000 
struct Edge{
	int start,end;
	int w; 
}e[10005]; 
int dis[105]; 

void Ford(){
	int u,v,flag;
	for(int i=1;i<n;++i){//最多执行n-1轮
		flag=0;
		for(int j=1;j<=m;++j){
			u=e[j].start;
			v=e[j].end; 
			if(dis[u]+e[j].w<dis[v]){
				dis[v]=dis[u]+e[j].w;
				flag=1;
			} 
		} 
		if(flag==0) break; 
	}
} 

int main(){
	cin>>n>>m;
	int x,y,wi;
	for(int i=1;i<=m;++i){
		cin>>x>>y>>wi;
		e[i]=(Edge){x,y,wi};
	}
	for(int i=1;i<=n;++i){
		dis[i]=inf;
	}
	cin>>s;//起点 
	dis[s]=0;
	//bellman-Ford
	Ford();
	for(int i=1;i<=n;++i){
		cout<<dis[i]<<" ";
	}
	return 0;
} 

检验是否有负权回路:如果进行了n轮还能被松弛,就说明有负权回路

C 复制代码
#include<bits/stdc++.h>
using namespace std;
#define inf 1001 

//边集数组----->带权有向图为例
int n,m,s; //n<=100  -1000<边权<1000 
struct Edge{
	int start,end;
	int w; 
}e[10005]; 
int dis[105]; 

bool Ford(){
	int u,v,f=0;
	for(int i=1;i<=n;++i){//执行n轮
		for(int j=1;j<=m;++j){
			u=e[j].start;
			v=e[j].end; 
			if(dis[u]+e[j].w<dis[v]){
				dis[v]=dis[u]+e[j].w;
                if(i==n) f=1;
			} 
		}
	}
    if(f==1) return true;
    else return false;
} 

int main(){
	cin>>n>>m;
	int x,y,wi;
	for(int i=1;i<=m;++i){
		cin>>x>>y>>wi;
		e[i]=(Edge){x,y,wi};
	}
	for(int i=1;i<=n;++i){
		dis[i]=inf;
	}
	cin>>s;//起点 
	dis[s]=0;
	//bellman-Ford
	if(Ford()){
        cout<<"有负环"<<endl;
	}
    else{
      for(int i=1;i<=n;++i){
          cout<<dis[i]<<" ";
      }  
    }
	return 0;
} 

优化:SPFA

最好时间复杂度 O(|E|) 平均时间复杂度会小 最坏时间复杂度不变O(|V||E|)

不稳定

在题目所设图中不出现负边权时,各大比赛的出题人均会卡SPFA算法,因为SPFA算法玄学的时间复杂度,给人一种骗分的感觉,所以为了尊重选手,尊重Diikstra算法,会卡SPFA.即SPFA已经死。

优先Dijkstra 有负边权再使用SPFA

SPFA能判断带环负权图

例题

P3371 【模板】单源最短路径(弱化版) - 洛谷

需要判断负环

C++ 复制代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long ;

//链式前向星写Dijkstra 
int n,m,cnt; //n<100 0<权值<1000
int h[100005];
struct Edge{
	int to,w,next;
}e[500005]; //100个点 最多100*(100-1)条边 
ll dis[100005];
int cur[100005];
int v[100005]; 
 
void add(int u,int v,int w){
	e[cnt].to=v;
	e[cnt].w=w;
	e[cnt].next=h[u]; 
	h[u]=cnt;
	cnt++;
}

bool spfa(int x){//堆优化 
	for(int i=1;i<=n;++i) {
		dis[i]=INT_MAX;
	}
	dis[x]=0;
	queue<int> q;
	int u,k;
	v[x]=1;
	q.push(x);
	while(!q.empty()){
		u=q.front();
		q.pop();
		v[u]=0;
		for(int i=h[u];i!=-1;i=e[i].next){
			k=e[i].to;
			if(dis[k]>dis[u]+e[i].w){
				dis[k]=dis[u]+e[i].w;
				cur[k]=cur[u]+1;
				if(cur[k]>=n) return false;
				if(v[k]==0){
					q.push(k);
					v[k]=1;
				}
			}
		}
	} 
	return true;
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	memset(h,-1,sizeof h);
	int s;
	//scanf("%d %d %d",&n,&m,&s);
	cin>>n>>m>>s; 
	int x,y,w; 
	for(int i=1;i<=m;++i){
		//scanf("%d %d %d",&x,&y,&w);
		cin>>x>>y>>w;
		add(x,y,w); 
	} 
	if(!spfa(s)){
		int ans=INT_MAX;
		cout<<ans<<endl;
		return 0;
	}else{
		for(int i=1;i<=n;++i){
			//printf("%d ",dis[i]);
			cout<<dis[i]<<" ";
		}
	}
	
	return 0;
} 
相关推荐
吃饭只吃七分饱1 小时前
C++第8章:IO库
开发语言·c++
闪电麦坤951 小时前
数据结构:字符串(Strings)
数据结构
宴之敖者、1 小时前
数组——初识数据结构
c语言·开发语言·数据结构·算法
青小莫1 小时前
c语言-数据结构-二叉树OJ
c语言·开发语言·数据结构·二叉树·力扣
典学长编程1 小时前
Java从入门到精通!第十一天(Java常见的数据结构)
java·开发语言·数据结构
胡斌附体2 小时前
oracle查询数据结构滤涉及的sql语句
数据结构·sql·oracle
不想学习\??!3 小时前
c练习-c基础
数据结构·算法
伊织code3 小时前
OpenCV 官翻8 - 其他算法
人工智能·opencv·算法·拼接·光流·成像
Gappsong8744 小时前
Rufus:Ubuntu U盘启动盘制作工具详解
linux·c++·web安全·网络安全
এ᭄画画的北北5 小时前
力扣-198.打家劫舍
算法·leetcode