Dijkstra?spfa?SPstra?

带负权的无负环最短路问题

对于一张有负边权的图,普通 Dijkstra 就不能用了,比如:
在这里插入图片描述
正常的 Dijkstra 扩散的节点依次为 1,3,2,41,3,2,41,3,2,4
这时候可以发现,当点 222 扩散的时候,原本达到点 333 的路径长度是 111,路径 1⟶2⟶31\longrightarrow2\longrightarrow3123 的权值之和为 −2-22,明显更短,这个时候点111 到点 333 的路径长度就会被更新为 −2-22,但是,点 333 在点 222 之前已经被扩散过,不会在进行第二次扩散。此时,点 111 到点 444 的路径长度依旧是 1+2=31+2=31+2=3

这里说明了 Dijkstra 的一个缺陷,贪心思路会考虑不到后续的负数。

那么,我们可以对 Dijkstra 做一个改动,解决现在遇到的问题。
上述的问题发生的根源就是 visvisvis 数组,在当前点找到更优值的时 visvisvis 数组不会改变,就导致了上图中点 444 没有被更新,所以我们在找到更优值时就再给当前点一个机会。

if(dis[y]>dis[x]+z){
	vis[y]=0;
	dis[y]=dis[x]+z;
}

这样可以很好地解决上图中出现的问题,但是判断负环还是需要 SPFA
那么,我们将上述的改动版 Dijkstra 命名为SPstra
在这里插入图片描述

带负环的最短路问题

这个时候就可以考虑用到 SPFA。
SPFA 与 Dijkstra 的不同点:

入队条件

Dijkstra 的遍历概念是:扩散
而 SPFA 的遍历概念是:筛选
SPFA 的标记表示的是“是否在队列中”,也就是说有没有看过这个元素值更新后对相连点的影响。
如果没有在队列中,并且还找到了当前点更优的方案,那么当前点就该入队上班了。(可以借助文章最顶上的图来理解)。

出队标记

出队后,元素就不在队列中了,标记取消。

负环判断

负环是什么?
负环就是一个有向图中的环,并且对这个环遍历一圈得到的权值总和为负,那么这个时候就可以一直绕着这个环转圈圈,使权值总和越来越小。
我们假设:一张有 nnn 个点的有向图中有这样一个点 xxx,这个点十分有魅力,图中其余的 n−1n-1n1 个点都有一条边连向这个点。

再极端一点,这其余的 n−1n-1n1 个点按照被遍历到的顺序依次对点 xxx 有更优贡献。
那么,这个点就会被入队 n−1n-1n1 次,这个时候还没有出现负环。
但是,如果这个点再被入队一次,入队次数达到了 nnn 次,就一定有一个点(暂且称为点 yyy)连接点 xxx 的这条路径被遍历了两次,这个时候就有了负环,因为点 xxx 在被点 yyy 连接的情况下还有一条负数边权和的路径连接着点 yyy

SPFA 可否使用优先队列优化

其实使用优先队列优化的 SPFA 就是前面的 “SPstra” 加上一个负环判断。

对于这个问题,答案是:能,但是很极端。

SPFA 优先队列优化的适用场景

对于正边权居多的图,可以使用优先队列优化,这样可以使效率接近 Dijkstra 的 O(Nlog⁡N)O(N\log N)O(NlogN)

普通 SPFA 的适用场景

对于负边权居多的图,其实普通队列和优先队列的遍历次数都是差不多的,只是顺序变了,而且优先队列还多了一个 O(log⁡N)O(\log N)O(logN),效率还不如普通队列。

总结

考试或者比赛时,题目的数据我们无从得知。
但我们知道的是,造数据的人们都是阴险狡诈忠恳善良的,我们可以猜疑相信他们。

posted on 2025-08-08 22:26  fish2012  阅读(12)  评论(0)    收藏  举报  来源