【题目来源】
https://www.acwing.com/problem/content/854/
【题目描述】
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
【输入格式】
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
【输出格式】
如果图中存在负权回路,则输出 Yes,否则输出 No。
【数据范围】
1≤n≤2000,
1≤m≤10000,
图中涉及边长绝对值均不超过 10000。
【输入样例】
3 3
1 2 -1
2 3 4
3 1 -4
【输出样例】
Yes
【算法分析】
● SPFA 算法,全称为 Shortest Path Faster Algorithm,是求解单源最短路径问题的一种常用算法,它可以处理有向图或者无向图,边权可以是正数、负数,但是不能有负环。
● Dijkstra 算法不能处理负权边。SPFA 算法能处理负权边。
● SPFA 算法是判断负环的常用算法,其实现如下: 在更新 dis[i] 时,我们顺带维护一个 cnt[i] 数组,该数组用来存放最短路的边数。如果 cnt[i]≥n 时,说明图中有 n+1 个点,而题目一共给了 n 个点,说明一定存在环。而 SPFA 算法更新的要求是距离变短,因此这个环一定是负环。 但是从原点开始可能走不到负环,我们需要初始时把所有的点都放入队列中。
● 链式前向星:https://blog.csdn.net/hnjzsyjyj/article/details/127190456
● 本题代码与 https://blog.csdn.net/hnjzsyjyj/article/details/138425339 及其类似。
【算法代码】
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int val[maxn],e[maxn],ne[maxn],h[maxn],idx;
int dis[maxn],cnt[maxn];
bool st[maxn];
int n,m;
void add(int a,int b,int w) {
val[idx]=w,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int spfa() {
queue<int> Q;
for(int i=1; i<=n; i++) {
Q.push(i);
st[i]=true;
}
while(!Q.empty()) {
int t=Q.front();
Q.pop();
st[t]=false;
for(int i=h[t]; i!=-1; i=ne[i]) {
int j=e[i];
if(dis[j]>dis[t]+val[i]) {
dis[j]=dis[t]+val[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) return true;
if(!st[j]) {
Q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main() {
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--) {
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
if(spfa()) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
/*
in:
3 3
1 2 -1
2 3 4
3 1 -4
out:
Yes
*/
【参考文献】
https://zhuanlan.zhihu.com/p/353019102
https://www.acwing.com/solution/content/47218/
https://blog.csdn.net/hnjzsyjyj/article/details/138425339
https://www.acwing.com/video/284/