【模板】负环
题目描述
给定一个 n n n 个点的有向图,请求出图中是否存在从顶点 1 1 1 出发能到达的负环。
负环的定义是:一条边权之和为负数的回路。
输入格式
本题单测试点有多组测试数据。
输入的第一行是一个整数 T T T,表示测试数据的组数。对于每组数据的格式如下:
第一行有两个整数,分别表示图的点数 n n n 和接下来给出边信息的条数 m m m。
接下来 m m m 行,每行三个整数 u , v , w u, v, w u,v,w。
- 若 w ≥ 0 w \geq 0 w≥0,则表示存在一条从 u u u 至 v v v 边权为 w w w 的边,还存在一条从 v v v 至 u u u 边权为 w w w 的边。
- 若 w < 0 w < 0 w<0,则只表示存在一条从 u u u 至 v v v 边权为 w w w 的边。
输出格式
对于每组数据,输出一行一个字符串,若所求负环存在,则输出 YES
,否则输出 NO
。
样例 #1
样例输入 #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
提示
数据规模与约定
对于全部的测试点,保证:
- 1 ≤ n ≤ 2 × 1 0 3 1 \leq n \leq 2 \times 10^3 1≤n≤2×103, 1 ≤ m ≤ 3 × 1 0 3 1 \leq m \leq 3 \times 10^3 1≤m≤3×103。
- 1 ≤ u , v ≤ n 1 \leq u, v \leq n 1≤u,v≤n, − 1 0 4 ≤ w ≤ 1 0 4 -10^4 \leq w \leq 10^4 −104≤w≤104。
- 1 ≤ T ≤ 10 1 \leq T \leq 10 1≤T≤10。
提示
请注意, m m m 不是图的边数。
原题
代码
#include <bits/stdc++.h>
using namespace std;
#define max_Heap(x) priority_queue<x, vector<x>, less<x>>
#define min_Heap(x) priority_queue<x, vector<x>, greater<x>>
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<long long, long long> PLL;
const double PI = acos(-1);
const int maxn = 2e3 + 6;
const int maxm = 3e3 + 6;
int n, m; // 点的个数,有向边的个数
struct edge
{
int to, len; // to为边的指向,len为边的长度即边权
};
vector<edge> e[maxn]; // 存储以点i为起点的边
ll minDis[maxn]; // 从起点到第i个点的最短路长度
bool vis[maxn]; // 第i个点是否在队列中
int cnt[maxn]; // 记录某个点的入队次数,若次数大于等于n,存在负环
bool spfa()
{
memset(minDis, 0x3f, sizeof(minDis));
memset(vis, 0x00, sizeof(vis));
memset(cnt, 0x00, sizeof(cnt));
minDis[1] = 0; // 按照题目要求,以一号点作为起点
queue<int> q;
q.push(1);
vis[1] = 1;
cnt[1]++;
while (!q.empty())
{
int st = q.front();
q.pop();
vis[st] = 0; // 已出队,vis赋值0
for (edge eg : e[st]) // 遍历以st为起点的所有有向边
{
int ed = eg.to;
int weight = eg.len;
if (minDis[st] + weight < minDis[ed]) // 判断是否可以更新最短路长度
{
minDis[ed] = minDis[st] + weight;
if (!vis[ed]) // 如果已经在队列中,则不需要再加入队列
{
q.push(ed);
vis[ed] = 1; // 记录在队列中
cnt[ed]++; // 加入队列的次数++
if (cnt[ed] >= n) // 入队次数>=n,存在负环,返回0
{
return 0;
}
}
}
}
}
return 1; // 不存在负环,返回1
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
for (int i = 1; i < maxn; i++)
{
e[i].clear();
}
cin >> n >> m;
int u, v, w; // 从u到v的权值为w的有向边
while (m--)
{
cin >> u >> v >> w;
if (w < 0)
e[u].push_back({v, w});
else
{
e[u].push_back({v, w});
e[v].push_back({u, w});
}
}
if (spfa())
cout << "NO" << '\n';
else
cout << "YES" << '\n';
}
return 0;
}