题目地址:
https://www.luogu.com.cn/problem/P3385
题目描述:
给定一个
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≥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
≤
n
≤
2
×
1
0
3
,
1
≤
m
≤
3
×
1
0
3
1 \le n \le 2 \times 10^3,1≤m≤3×10^3
1≤n≤2×103,1≤m≤3×103
1
≤
u
,
v
≤
n
,
−
1
0
4
≤
w
≤
1
0
4
1≤u,v≤n,−10^4 ≤w≤10^4
1≤u,v≤n,−104≤w≤104
1
≤
T
≤
10
1≤T≤10
1≤T≤10。
可以用SPFA来做。由于要找的是从 1 1 1号点能走到的负环,所以一开始队列里只需要将 1 1 1入队,并且初始化距离的时候除了 d [ 1 ] d[1] d[1]初始化为 0 0 0之外,别的点都要初始化为 + ∞ +\infty +∞。其余步骤参考https://blog.csdn.net/qq_46105170/article/details/113821651。代码如下:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 2010, M = 3010 * 2;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
int dist[N], cnt[N];
bool st[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
bool spfa() {
queue<int> q;
// 只需把起点1入队
q.push(1);
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
memset(cnt, 0, sizeof cnt);
memset(st, 0, sizeof st);
while (q.size()) {
int t = q.front(); q.pop();
st[t] = false;
for (int i = h[t]; ~i; 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) return true;
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main() {
int T;
cin >> T;
while (T--) {
// 初始化一下邻接表
memset(h, -1, sizeof h);
idx = 0;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
if (c >= 0) add(b, a, c);
}
printf("%s\n", spfa() ? "YES" : "NO");
}
}
时间复杂度 O ( m n ) O(mn) O(mn)(判断负环的时候spfa的时间复杂度是比较高的),空间 O ( n ) O(n) O(n)。