题目描述
给定一个 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
模板
//创建一个不存在重复元素的队列(使用一个布尔型数组来辅助保证元素不重复)
queue<int>q;
bool sta[N];
//多创建一个cnt[N]数组用于存储每个点到起点的步数
void spfa(){
//不需要初始化dist数组,因为不需要查找最短路径
//将所有点入队(因为图中可能有多个连通块,所以不能只考虑起点所在联通块)
for(...){
q.push(i);
sta[i]=true;
}
//队列不为空
while(!q.empty()){
//队首元素t出队,由于队首元素出队,所以将其状态值设置为false
//使用t更新与之相关点
for(...){
if(dist[u]>dist[t]+w[i]){
dist[u]=dist[t]+w[i];
//计算该点的cnt,如果该点到起点的步数>=n,则说明存在负环
cnt[j]=cnt[t]+1;
if(cnt[j]>=n){
cout<<"Yes"<<endl;
return;
}
//如果点u的dist被更新,并且队列中没有u,将u入队,并改变其状态(与常规SPFA模板相同)
}
}
}
//否则不存在负环
cout<<"No"<<endl;
}
C++ 代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e4+5,N=2e4+10;
int n,m,e[M],ne[M],h[N],idx,dist[N],w[M],cnt[N];
queue<int>q;
bool sta[N];
void add(int a,int b,int c){
w[idx]=c;
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void spfa(){
for(int i=1;i<=n;i++){
q.push(i);
sta[i]=true;
}
while(!q.empty()){
int t=q.front();
q.pop();
sta[t]=false;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n){
cout<<"Yes"<<endl;
return;
}
if(!sta[j]) q.push(j);
}
}
}
cout<<"No"<<endl;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
}
spfa();
return 0;
}