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\)
证毕!
其实以上的思想还可以应用到 费用流
中,将 ek
的 SPFA
改成 dij
,节省时间(诶好像就是原始对偶
的核心......)