最短路算法(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;
}

浙公网安备 33010602011771号