题解 P3385 【【模板】负环】

Suuon_Kanderu

2020-08-23 15:20:52

Solution

模板题为啥没人来发题解啊…… 反正 SPFA 也已经是 $O(VE)$ 了,所以我们直接写 BellmanFord 也只是差个常数而已。 BellmanFord 的算法流程是这样的: 1. 初始化,设 $d_i$ 为 1 到 n 的最短路距离,肯定是 $d_1 = 0, d_{2 \cdots n} = \operatorname{inf}$。 2. 进行 $n-1$ 次松弛操作。我们每次就更新一层数的最短路。什么意思呢? ![](https://cdn.luogu.com.cn/upload/image_hosting/qhc5gpzq.png) 比如这个垃圾图,第一次,我们就会把 $d_B$ 更新为 $f$ ,第二次操作时就会把 $d_C$ 更新为 $f+g$。 具体实现就可以用 for 循环扫一遍所有的边。当然你用什么 SPFA 的就是用队首更新点。 3. 到了我们判断负环的时间了。 我们把所有边扫一遍,由于题目说 **从顶点 1 出发能到达的负环**,所以我们直接掠过顶点 1 无法到达的点,($d_i = \operatorname{inf}$)。按理说,$n-1$ 次松弛之后我们一定无法获得更短的路了(都被松弛过了)。然而,如果存在负环,就没有最短路了,因为在负环上能够越走越短,**所以此时如果能够松弛,一定存在负环** 代码: ``` #include <cstdio> #include <iostream> #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 1000; int cnt = 0; struct node{ int x , y , v; }e[N]; void add(int x ,int y , int v) {e[++cnt] = {x , y , v};} void addd(int x, int y , int v) { if(v < 0)add(x , y , v); if(v >= 0) add(x , y , v) , add(y , x , v); } int n; bool bellman() { static int d[N]; d[1] = 0; for(int i = 2; i <= n; i++) d[i] = 0x7fffffff; for(int i = 1 ; i <= n - 1; i++) for(int j = 1; j <= cnt; j++) { if(d[e[j].x] != 0x7fffffff && d[e[j].x] + e[j].v < d[e[j].y]) d[e[j].y] = d[e[j].x] + e[j].v; } for(int i = 1; i <= cnt; i++) { if(d[e[i].x] == 0x7fffffff || d[e[i].y] == 0x7fffffff)continue; if(d[e[i].x] + e[i].v < d[e[i].y])return true;// 负权回路 } return false; } signed main() { int t;scanf("%d" , &t); while(t--) { memset(e , 0 , sizeof(e)); cnt = 0; int m;scanf("%d%d" , &n, &m); for(int i = 1; i <= m; i++) { int x , y , v;scanf("%d%d%d" , &x , &y , &v); addd(x , y , v); } if(bellman())printf("YES\n"); else printf("NO\n"); } return 0; } ```