P3385 【模板】负环
题意:
给定一个 n 个点的有向图,请求出图中是否存在从顶点 1 出发能到达的负环。
负环的定义是:一条边权之和为负数的回路。
题解:
先说结论:
判断给定的有向图中是否存在负环。
利用 spfa 算法判断负环有两种方法:
1) spfa 的 dfs 形式,判断条件是存在一点在一条路径上出现多次。
2) spfa 的 bfs 形式,判断条件是存在一点入队次数大于总顶点数。
判断负环一般用BF或者spfa
BF是通过不但迭代计算最短路,因为存在负环,则不可能计算出最短路,就会不停的迭代,正常迭代n-1次就结束,若第n次迭代仍然有结点的最短路能被更新,说明图中有负环。复杂度为O(nm)
spfa是队列优化版的BF。SPFA因为使用队列,所以没办法直接知道目前进行第几轮迭代。BF的第i轮迭代实际上就是计算最短路包含i条边的节点。所以我们用cnt[x]表示1到x的最短路包含的边数,cnt[1] = 0,每次用dis[x] + w[x][y]更新dis[y]时,就用cnt[x] + 1来更新cnt[y].如果cnt[y] > =n,说明最短路包含n个边,存在负环
另一个方法是记录每个点的入队次数来判断负环,若有节点入队次数> = n,则存在负环。(这个方法慢)
代码为SPFA的第一种方法
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
struct edge{int v,w;edge(int v=0,int w=0):v(v),w(w){};
};
const int maxn=3e3+9;
vector<edge>e[maxn];
int dis[maxn],cnt[maxn],vis[maxn];
int n,m;
bool spfa(int s){queue<int>q;memset(dis,0x3f,sizeof dis);memset(cnt,0,sizeof cnt);memset(vis,0,sizeof vis);q.push(s);vis[s]=1;dis[s]=0;while(!q.empty()){int u=q.front();q.pop();vis[u]=0;for(int i=0;i<e[u].size();i++){int v=e[u][i].v;int w=e[u][i].w;if(dis[u]+w<dis[v]){dis[v]=dis[u]+w;cnt[v]=cnt[u]+1;if(cnt[v]>=n)return 1;if(vis[v]==0){q.push(v);vis[v]=1;}}} }return 0;
}
int main()
{int t;cin>>t;while(t--){cin>>n>>m;for(int i=1;i<=n;i++)e[i].clear();for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;e[u].push_back(edge(v,w));if(w>=0)e[v].push_back(edge(u,w));}if(spfa(1))cout<<"YES"<<endl;else cout<<"NO"<<endl; }
}
BFS-SPFA 与 DFS-SPFA 的优劣
SPFA 有 BFS 和 DFS 两种实现方式,如果仅仅要判断是否存在负环,DFS-SPFA 要比 BFS-SPFA 快上很多。但是在没有负环时要求出解,DFS-SPFA 会比 BFS-SPFA 慢很多。
BFS:BFS处理环能力较弱,所以该方法若遇到负环有可能TLE
bool spfa(int org){memset(dis, 60, sizeof(dis));int i;queue<int> Q;dis[org] = 0, vis[org] = true, Q.push(org);while(!Q.empty()){int u = Q.front(); Q.pop();vis[u] = false;for(i = head[u]; i; i = e[i].next){int v = e[i].to, w = e[i].w;if(dis[v] > dis[u] + w){dis[v] = dis[u] + w;cnt[v] = cnt[u] + 1;if(cnt[v] == N + 1) return false;if(!vis[v]) Q.push(v), vis[v] = true;}}}return true;
}
DFS:处理最短路能力较若弱,一般针对负环的题目
由于DFS-SPFA可以在找到负环后及时退出,所以不会像BFS-SPFA那样TLE
bool spfa(int u){vis[u] = true;int i;for(i = head[u]; i; i = e[i].next){int v = e[i].to, w = e[i].w;if(dis[v] > dis[u] + w){dis[v] = dis[u] + w;if(vis[v]) return false;if(!spfa(v)) return false;}}vis[u] = false;return true;
}