SPFA判负环
【前言】
最短路的求法中最广为人知的我仅仅知道的,有弗洛伊德,dijkstra和SPFA。
弗洛伊德最简单的三重循环,复杂度n^3,一般也就做个n小的题目,遇到n大一点的(超过1000)几乎就只能拿部分分。
SPFA是一种广为人知的已经死掉的算法(某人说过一道图论题如果不卡卡SPFA就不是一道好的图论题目),但是他却有一个很重要的作用,是其他三者无法代替的,那就是这篇博客的题目,判负环!
dijkstra是这三种里面最为稳定的一种算法,何为稳定,弗洛伊德只能跑那么小的数据范围就可以看出来,在很多题目上面只能打一下暴力,SPFA就更不用多说了他死了,所以dijkstra理所当然的成为了最稳定的算法。
说这么多就是为了引出SPFA判负环!
【不可代替性】
dijkstra和弗洛伊德都是判断不了负环的
弗洛伊德就是那么个枚举中间点的方法
根本不知道到底有没有负环
就那样求出来了一个最终结果
如果有负环那结果可能会是错误的
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判负环板子题
详情解释请看
这里