题目描述
给定一个 nnn 个点的有向图,请求出图中是否存在从顶点 111 出发能到达的负环。
负环的定义是:一条边权之和为负数的回路。
输入格式
本题单测试点有多组测试数据。
输入的第一行是一个整数 TTT,表示测试数据的组数。对于每组数据的格式如下:
第一行有两个整数,分别表示图的边数 nnn 和接下来给出边信息的条数 mmm。
接下来 mmm 行,每行三个整数 u,v,wu, v, wu,v,w。
- 若 w≥0w geq 0w≥0,则表示存在一条从 uuu 至 vvv 边权为 www 的边,还存在一条从 vvv 至 uuu 边权为 www 的边。
- 若 w<0w < 0w<0,则只表示存在一条从 uuu 至 vvv 边权为 www 的边。
输出格式
对于每组数据,输出一行一个字符串,若所求负环存在,则输出 YES
,否则输出 NO
。
输入输出样例
输入 #1
2 3 4 1 2 2 1 3 4 2 3 1 3 1 -3 3 3 1 2 3 2 3 4 3 1 -8
输出 #1
NO YES
负环即边权和为负数的环。
重要性质:两点之间存在负环时,必然没有最短路。(因为路径越更新越小
原理:lyd的书上有言:从起点到x的最短路包含了>=n条边,说明肯定有节点被重复经过了,故最短路径上存在环且环上各个点能更新下一个点的dist值。
设cnt[x]表示从1到x的最短路包含的边数,当更新d[y]=d[x]+z时也同时更新cnt[y]=cnt[x]+1。当有cnt[y]>=n时直接退出即可。
#include <bits/stdc++.h> #define N 2005 #define M 3005 using namespace std; int n,m,tot=0; int head[N],edge[2*M],ver[2*M],Next[2*M],cnt[N],d[N]; bool v[N]; void add(int x,int y,int z) { ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; } bool spfa() { queue<int>q; d[1]=0,v[1]=1,cnt[1]=0; q.push(1); while(q.size()) { int x=q.front(); q.pop(); v[x]=0; int i; for(i=head[x];i;i=Next[i]) { int y=ver[i],z=edge[i]; if(d[y]>d[x]+z) { d[y]=d[x]+z; cnt[y]++; if(cnt[y]>=n)return 1; if(!v[y])q.push(y),v[y]=1; } } } return 0; } int main() { int t; cin>>t; while(t--) { memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); memset(ver,0,sizeof(ver)); memset(Next,0,sizeof(Next)); memset(cnt,0,sizeof(cnt)); memset(v,0,sizeof(v)); memset(d,0x3f,sizeof(d)); tot=0; cin>>n>>m; int i,u,v,w; for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); if(w>=0)add(v,u,w); } if(spfa())cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }