前言
spfa
可以用来判断负环。
一、题目陈述
二、解决思路
dist
数组存储当前1号点到n号点的最短距离,cnt
数组表示当前最短路的边数。每次更新的时候,维护两个数组:
dist[x]=dist[t]+w[i];
cnt[x]=cnt[t]+1;
如果在某次维护的过程中,发现cnt[x]>=n
,说明1到x的最短路中经过了n+1个点,根据抽屉原理,一定至少有两个点相同,即存在一个环;而且如果这个环的权不是负数,就不会存在这个环,所以必然存在负环。
三、代码实现
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=1e5+10,M=2*N;
int n,m;
// 邻接表的数据结构
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
int dist[N];
int cnt[N];
// 存储某一个点是否在队列中,存重复的点没有意义
bool st[N];
bool spfa() {
// 只判断是否存在负环时不需要进行初始化
// memset(dist,0x3f,sizeof dist);
// dist[1]=0;
queue<int> q;
// q.push(1);
// st[1]=true;
// 把所有点全部放到队列中,因为负环可能从任何点伸出
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]) {
q.push(j);
st[j]=true;
}
}
}
}
// 只判断是否存在负环时不需要以下这句
// if(dist[n] == 0x3f3f3f3f) return -1;
return false;
}
int main() {
cin>>n>>m;
memset(h,-1,sizeof(h));
for(int i=0;i<m;i++) {
int a,b,c; cin>>a>>b>>c;
add(a,b,c);
}
if(spfa()) puts("Yes");
else puts("No");
return 0;
}
总结
spfa
算法判断负环相比于spfa
无非是多了一个cnt
数组和鸽巢原理的应用。