Johnson全源最短路

SPFA 的优化:

(虽然它已经死了,但它还活着)

SLF 优化:我们将队列换成双端队列 deque,当要新插入一个元素时,如果这个元素的距离大于队首元素,就正常将它插到队尾,否则插到队首

SLF + swap 优化:在 SLF 的基础上,每次队列发生改变的时候,我们就比较队首和队尾的距离,如果 \(dis[front]>dis[back]\) ,就交换队首队尾的元素

但考场上如果没有负边权的存在,还是好好用 dij 吧!


引入

当我们想要求任意两点的最短路,我们会怎么做?

我们可以考虑用 \(O(n^3)\)Floyd

但显然这不够,当 \(n\) 来到上千的时候,我们就寄了

那如果我们跑 \(n\) 次 优先队列优化的 dij ,不就可以做到 \(O(nmlogm)\) 了吗

这就是 Johnson全源最短路 想要干的事

正常来说,没有负权的情况下,这样直接跑是没问题的

但如果有了负权,dij 的正确性就会出问题了

那我们是不是给所有边权都加上一个固定值 \(add\) 转正,最后求出最短路后减去 \(k\)\(add\) 就行了呢?

显然不是的,这里引用一下 OI-Wiki 的图:

如图,这里本来 \(1-2\) 的最短路应该是 \(1-5-3-2\) ,长度为 \(-2\)

但如果我们将边权都加 \(5\)

\(1-2\) 的最短路就是 \(1-4-2\) 了?显然错了

那我们就要考虑换一种思路了


实现

(其他的算法讲解上都讲到了势能,其实我觉得没什么必要,因此这里不做解释,想要看分析的可以移步到 OI-Wiki\(\color{White} 其实还是懒\)

我们先弄出一个虚拟结点 \(0\),并向其他所有结点连一条边权为 \(0\) 的边

我们以 \(0\) 为起点跑一遍 \(SPFA\) ,假设 \(0\)\(u\) 的最短路为 \(h[u]\)

我们将原来的所以边 \((u,v)\) 加上 \(h[u]-h[v]\),就可以正常跑 dij 了!

只需要将 dij 跑出来的最短距离 \(dis[t]\) 减去 \(h[s]-h[t]\)\(s\) 是起点)即可

为什么这样是正确的呢?

我们考虑一条路径 \(s-u_1-u_2-...-u_k-t\)

在我们处理过路径长度后,得到 \(dis[s,t]=(r[s,u_1]+h[s]-h[u_1])+(r[u_1,u_2]+h[u_1]-h[u_2])+...+(r[u_k,t]+h[u_k]-h[t])\)

化简得 \(dis[s,t]=r[s,u_1]+r[u_1,u_2]+...+r[u_k,t]+h[s]-h[t]\)

这不就是原路径 + \(h[s]\) - \(h[t]\)

所以就不会出现不符合原路径的情况了!

那为什么处理后的边就不会存在负权了呢?

因为在跑 \(SPFA\) 时,我们就清楚的知道 \(h[u]+r[u,v]\ge h[v]\)

所以 \(h[u]+r[u,v]-h[v]\ge 0\)

证毕!

其实以上的思想还可以应用到 费用流 中,将 ekSPFA 改成 dij ,节省时间(诶好像就是原始对偶的核心......)

代码

posted @ 2022-03-10 13:46  zuytong  阅读(47)  评论(0编辑  收藏  举报