持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
【模板】负环
题目描述
给定一个 个点的有向图,请求出图中是否存在从顶点 出发能到达的负环。
负环的定义是:一条边权之和为负数的回路。
输入格式
本题单测试点有多组测试数据。
输入的第一行是一个整数 ,表示测试数据的组数。对于每组数据的格式如下:
第一行有两个整数,分别表示图的点数 和接下来给出边信息的条数 。
接下来 行,每行三个整数 。
- 若 ,则表示存在一条从 至 边权为 的边,还存在一条从 至 边权为 的边。
- 若 ,则只表示存在一条从 至 边权为 的边。
输出格式
对于每组数据,输出一行一个字符串,若所求负环存在,则输出 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
提示
数据规模与约定
对于全部的测试点,保证:
- ,。
- ,。
- 。
提示
请注意, 不是图的边数。
解题思路 SPFA
如果存在负环,那么就会有负边,这样的话有些点就会被一直更新。所以如果一个点被超 个点更新过,那就说明有负环。
或者,可以记录最短路的边数。如果不存在负环,图的最短路是不会反复经过一条边的。
#include <cstdio>
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
int T, n, m, u, v, w;
vector <pair<int, int> > G[2001];
int dis[2001], cnt[2001];
bool in[2001];
void spfa(){
memset(dis, 0x3f3f, sizeof(dis));
memset(in, 0, sizeof(in));
memset(cnt, 0, sizeof(cnt));
queue <int> q;
q.push(1);
dis[1] = 0;
in[1] = true;
cnt[1] = 1;
while (!q.empty()){
u = q.front();
q.pop();
in[u] = false;
for (int i = 0; i < G[u].size(); i++){
v = G[u][i].first;
w = G[u][i].second;
if (dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if (!in[v]){
cnt[v]++;
q.push(v);
in[v] = true;
if (cnt[v] >= n){
printf("YES\n");
return;
}
}
}
}
}
printf("NO\n");
}
int main(){
scanf("%d", &T);
for (int t = 0; t < T; t++){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++){
G[i].clear();
}
for (int i = 0; i < m; i++){
scanf("%d%d%d", &u, &v, &w);
G[u].push_back(make_pair(v, w));
if (w >= 0){
G[v].push_back(make_pair(u, w));
}
}
spfa();
}
return 0;
}