什么是负环?
顾名思义,就是一个所有边的边权和为负数的环。
出现负环会怎么样?
我们知道,一般情况下,图上的最短路都是确定的。但是一旦图上有一个负环,
s
s
s 到
t
t
t 的最短路就会不远千里的去覆盖上这个环(只要能够到达),并且不厌其烦的走上一遍又一遍。由于负环的边权和是负的,并且它是一个环,也就是说走一遍和走无数遍都停留在进入的那个点。那么最短路每经过一次这个负环,这个费用都会缩小一点,如果经过了无数次,也就是无穷小,也就是不存在最短路。当然这里有一个限定,就是每个点经过的次数不能超过
1
1
1 次。
怎么判定负环?
根据负环的定义,我们知道在有负环的图中不存在最短路,因为你可以绕着负环一直跑,而你的路径和却会越来越少!
所以如果我们在有负环的图上跑SPFA,会陷入死循环的!
正因为这点,所以可以用SPFA来判断负环。
用 c n t [ x ] cnt[x] cnt[x] 表示 x x x 这个节点入队了几次(也就是 x x x 这个节点的最短路被更新了几次),若图中有 n n n 个节点,那么每个节点最多 n − 1 n−1 n−1 次入队,当入队次数,即 c n t [ x ] ≥ n cnt[x]≥n cnt[x]≥n 时,退出循环,并确定图中有负环。
模板题 代码(贺的)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N = 20010, M = 30010;
int t, n, m, tot, head[N], ver[M << 1], nex[M << 1], edge[M << 1], dis[N], vis[N], cnt[N], flag;
inline void add (int x, int y, int z) {
ver[++ tot] = y;
edge[tot] = z;
nex[tot] = head[x];
head[x] = tot;
}
inline void spfa () {
queue <int> q;
q.push(1);
vis[1] = 1;
dis[1] = 0;
while (!q.empty()) {
int x = q.front();
q.pop();
vis[x] = 0;
for (int i = head[x]; i; i = nex[i]) {
int y = ver[i], w = edge[i];
if (dis[y] > dis[x] + w) {
dis[y] = dis[x] + w;
if (!vis[y]) {
q.push(y);
vis[y] = 1;
cnt[y] ++;
if (cnt[y] >= n) {
flag = 1;
return;
}
}
}
}
}
}
int main () {
scanf("%d", &t);
while (t --) {
flag = 0;
memset(head, 0, sizeof(head));
memset(ver, 0, sizeof(ver));
memset(nex, 0, sizeof(nex));
memset(edge, 0, sizeof(edge));
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i ++) {
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
if (w >= 0) {
add(a, b, w);
add(b, a, w);
} else
add(a, b, w);
}
spfa();
if (flag)
printf("YE5\n");
else
printf("N0\n");
}
return 0;
}