最短路算法(Bellman-Ford&SPFA)

最短路算法(Bellman-Ford&SPFA)

可以判断图中是否有负环的最短路算法

松弛是Bellman算法核心操作,松弛对边进行更新,一次操作就是:

dis[v]=min(dis[v],dis[u],w[u][v]);

也就是用与 v 相邻且已被统计的节点 u 的最短路径来更新 v 的最短路径

Bellman就是通过不断对所有边进行松弛操作最终求出答案的,当一轮操作结束后没有任何一条边被更新时,那么松弛操作就结束了

在存在最短路的情况下每次松弛操作会使最短路边数至少增加一条,所以最多进行 n-1 次松弛操作,所以若出现负环,松弛会无限进行下去,当松弛操作来到第 n 次时,说明从起点出发可以抵达负环

注意:只能判断从这个起点出发是否能抵达负环,不能判断整个图是否存在负环

我们不难发现这样的时间开销还是很大的,我们进行了许多的无效操作,考虑优化

只有上一次被松弛的节点所连接的边才可能被这一次松弛影响

所以我们用队列来维护哪些节点可能会被松弛就减少了时间复杂度

这样的优化就是SPFA,但是还是有可能会被卡回 O(nm) 的时间复杂度,所以当没有负边时,尽量不要使用SPFA

P3385 【模板】负环 - 洛谷

判断负环是SPFA的经典运用,代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
struct Edge{
	int to;
	int w;
};
int T;
int n,m;
vector<Edge> g[N];
int dis[N];
int cnt[N];
bool vis[N];
bool spfa(){
	for(int i=1;i<=n;i++){
		vis[i]=0;
		cnt[i]=0;
		dis[i]=1e9;
	}
	queue<int> q;
	q.push(1);
	dis[1]=0;
	vis[1]=1;
	cnt[1]++;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(auto x:g[u]){
			int v=x.to;
			int w=x.w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				if(!vis[v]){
					q.push(v);
					vis[v]=1;
					cnt[v]++;
				}
				if(cnt[v]>=n) return true;
			}
		}
	}
	return false;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);     cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			g[i].clear();
		}
		for(int i=1;i<=m;i++){
			int u,v,w;
			cin>>u>>v>>w;
			g[u].push_back({v,w});
			if(w>=0) g[v].push_back({u,w});
		}
		if(spfa()){
			cout<<"YES"<<endl;
		}
		else{
			cout<<"NO"<<endl;
		}
	}
	return 0;
} 
posted @ 2025-07-21 16:40  Zom_j  阅读(9)  评论(1)    收藏  举报