给定一个 nn 个点 mm 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数 nn 和 mm。
接下来 mm 行每行包含三个整数 x,y,zx,y,z,表示存在一条从点 xx 到点 yy 的有向边,边长为 zz。
输出格式
如果图中存在负权回路,则输出 Yes
,否则输出 No
。
数据范围
1≤n≤20001≤n≤2000,
1≤m≤100001≤m≤10000,
图中涉及边长绝对值均不超过 1000010000。
题解:
spfa算法:只有被更新距离的点 才会更新其他点的距离
判断负环:最短路中有大于等于n条边的 则说明有负环
因为不一定负环是从一可以到达的 所以先将所有点加入队列
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N=2010;
const int M=10010;
int e[M],ne[M],h[N],idx,w[M];
int dis[N],cnt[N];//dis 当前1-x最短距离 cnt:当前这个最短距离所用的边数
bool st[N];
int n,m;
void add(int x,int y,int z)//建立邻接表
{
e[idx]=y,ne[idx]=h[x],w[idx]=z,h[x]=idx++;
}
bool spfa()
{
queue<int> q;
for(int i=1;i<=n;i++)//因为负环不一定是从一开始的 所以要把每一个点加入到队列中
{
q.push(i);
st[i]=true;
}
while(q.size())//遍历队列中所有点
{
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]+w[i])//如果更小 则更新
{
dis[j]=dis[t]+w[i];
cnt[j]=cnt[t]+1;//更新每个边数
if(cnt[j]>=n)return true;//超过n 证明有环 最短路中有环证明有负环
if(!st[j])
{
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
}
bool t=spfa();
if(t)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}