浅谈SPFA判负环

时间:2023-03-10 06:59:26
浅谈SPFA判负环

SPFA判负环

有不足的地方请指出

本蒟蒻一定会修改吼

【前言】

最短路的求法中最广为人知的我仅仅知道的,有弗洛伊德,dijkstra和SPFA。

弗洛伊德最简单的三重循环,复杂度n^3,一般也就做个n小的题目,遇到n大一点的(超过1000)几乎就只能拿部分分。

SPFA是一种广为人知的已经死掉的算法(某人说过一道图论题如果不卡卡SPFA就不是一道好的图论题目),但是他却有一个很重要的作用,是其他三者无法代替的,那就是这篇博客的题目,判负环!

dijkstra是这三种里面最为稳定的一种算法,何为稳定,弗洛伊德只能跑那么小的数据范围就可以看出来,在很多题目上面只能打一下暴力,SPFA就更不用多说了他死了,所以dijkstra理所当然的成为了最稳定的算法。

说这么多就是为了引出SPFA判负环!

【不可代替性】

dijkstra和弗洛伊德都是判断不了负环的

弗洛伊德可以判断负环

但是我不会

我也不想会

人家SPFA好不容易有一个活着的理由干嘛要给人家扼杀掉

dijkstra遇到负权边就死了

更别说负环了

但是SPFA可以判断负环

具体原因不多赘述了,学过dijkstra和弗洛伊德的应该都知道

但是我不知道

【具体实现】

SPFA的过程

每次都拿一个点到起点的距离来松弛其他的点到起点的距离

判负环

负环是一个边权值和等于负数的环

可以想想一下

如果SPFA遇到了负环会出现什么情况

一直松弛下去

因为每次出现的负数都可以让目前最短的边变得更短

所以可以根据这一点

开一个数组用来记录这是这一条链上第几个入队的数

然后每次松弛的时候都把到达的点入队的数标为前面这个点入队的次数+1,因为这是一个顺序的过程

就像是1,2,3……这样的数列

然后如果出现了负环就会和上面说的一样一直松弛下去

然后这个负环上的点入队的数就会不断变大

可以想一下

如果n个数连成一个点

那入队数最大才只能是n

所以只要某个点的入队数大于了n

那就可以证明他在不停得松弛

也就是出现了负环

【核心代码】

bool SPFA(int acioi)
{
queue<int>q;
for(register int i = 1;i <= n;++ i)
d[i] = 99999999;
d[acioi] = 0;
q.push(acioi);
while(!q.empty())
{
int x = q.front();
q.pop();use[x] = false;
for(register int i = head[x];i != 0;i = a[i].ne)
{
int y = a[i].y;
if(d[y] > d[x] + a[i].z)
{
d[y] = d[x] + a[i].z;
cnt[y] = cnt[x] + 1;
if(cnt[y] > n)
return false;
if(use[y] == false)
{
use[y] = true;
q.push(y);
}
}
}
}
return true;
}

【例题】

洛谷P2136 拉近距离

SPFA判负环板子题

详情解释请看

这里

洛谷P3385 【模板】负环

同样也是板子题

想请解释请看

这里