spfa算法判断负环
spfa求负环:
分析:为什么可以通过spfa求负环?
dist[x]表示x点距离源点的距离,cnt[x]表示从源点到x点经过的边数
有:
if(dist[x] > dist[t] + w[i]) { dist[x] = dist[t] + w[i]; cnt[x] = cnt[t] + 1; }
cnt[x] = cnt[t] + 1 的意思是 如果距离更新了,那么从源点到x的边数就等于源点到t的边数+ t到x的边数(即一条边)
所以通过这个我们可以判断是否存在负环,如果在x,t之间存在负环,那么cnt[x] 会不断加1,我们通过判断如果cnt[x] >= n 进而确定是否存在负环。
为什么是cnt[x] >= n ? 因为cnt数组表示的是边数,如果从源点到x点的边数大于等于n,那么在源点和x点之间肯定存在n+1个点,但是最多只有n个点,所以
必然有点重复出现,从而出现负环 !
同时我们并不知道出现负环的地方是否与源点连通,所以我们要将所有的点都提前放进队列中,这样就可以判断所有的点。
代码:
#include <iostream> #include <queue> #include <cstring> using namespace std; const int N = 100010; int e[N], ne[N], h[N], w[N], dist[N], idx, cnt[N]; bool st[N]; int n, m; 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; memset(dist, 0x3f, sizeof dist); for(int i = 1; i <= n; i++) { st[i] = true; q.push(i); } while(q.size()) { int t = q.front(); q.pop(); st[t] = false; for(int i = h[t]; i != -1; 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]) { st[j] = true; q.push(j); } } } } return false; } int main() { cin >> n >> m; memset(h, -1, sizeof h); while(m--) { int a, b, c; cin >> a >> b >> c; add(a, b, c); } if(spfa()) puts("Yes"); else puts("No"); return 0; }