最短路

Bellman-Ford

这是一种暴力求解单源最短路的方法。如果图不存在负环,那么任意两点之间的最短路一定不经过相同的点。

假设 \(A\)\(E\) 的最短路径为 \(A \to B \to C \to D \to E\),那么 \(A \to B \to C \to D\) 一定为 \(A\)\(C\) 的最短路。

\(dis_{x}\) 表示起点 \(s\)\(x\) 的最短路长度。假设当前已经有经过点数为 \(i\) 的最短路,那么对每条边 \(u \to v\) 都进行(记为松弛):\(dis_{v} = \min(dis_{v},dis_{u}+w)\) 的操作(初始 \(dis_{s}=0\),其余点 \(dis\)\(+\infty\))。这样一定能得到点数为 \(i+1\) 的最短路。进行 \(n-1\) 次松弛就能算出最短路了。

如果在进行第 \(n\) 次松弛时,还有点的 \(dis\) 值在变小,说明存在经过点数大于 \(n\) 的最短路,即整个图存在负环。

这种算法的时间复杂度为 \(O(nm)\)

SPFA

容易发现在 Bellman-Ford 中,存在许多无效的松弛,对于边 \(u \to v\),当 \(dis_{u}\) 从未被更新过或者 \(u \to v\) 进行过一次松弛后,\(dis_{u}\) 再也没被更新过,称此时进行的 \(u \to v\) 的是 \(No\) 的,否则称其是 \(Yes\) 的。

尝试去掉这样的无效松弛。维护一个队列,里面的点 \(u\) 进行的扩展(扩展指对一个 \(u\) 而言,\(u \to v\) 的所有松弛)是 \(Yes\) 的。每次取出队头 \(u\) 进行扩展,把更新成功且此时不在队列里的点 \(v\) 放进队列里。然后弹出 \(u\)

当队列为空时,说明所有 \(u \to v\) 的松弛全是 \(No\) 了,此时 \(dis_{i}\) 即为正确最短路。

如果存在负环,那么队列将一直非空。这样的情况该怎么判断呢?记 \(Len_{i}\) 表示当前 \(s\)\(i\) 的最短路经过的点数。如果松弛时存在一个点的 \(Len_{i}\) 大于 \(n\),就说明存在负环。

在一般图上速度很快,但能被卡成 \(O(nm)\)

Dijkstra

Dijkstra 算法适用于非负权图。直接介绍这个算法的过程和证明吧。

过程:将节点分成两个集合 \(S\)\(T\)\(S\) 表示已确定最短路的点集(即 \(dis_{i}\) 一定正确的点),\(T\) 表示 \(dis_{i}\) 不一定正确的点集。每次在 \(T\) 中选出一个最小的 \(dis\) 值的点,把该点放入 \(S\) 里,然后进行对它一次扩展。直到所有点都在 \(S\) 里。最开始把所有点放入 \(T\) 里,\(dis_{s}=0\),其余点 \(dis\)\(+\infty\)

用堆来实现,记 \(vis_{i}\) 表示 \(i\) 是否在 \(S\) 里,每次取出堆顶 \(x\),如果 \(vis_{x}\)\(0\),对 \(x\) 做一遍扩展,如果 \(dis_{y}\) 成功更新就加入堆里。然后弹出 \(x\)。用 \(vis\) 的原因是同一个点会 \(x\) 多次入堆,但是第一次取出时才进行扩展。

由于每个点只可能进行一次扩展,总共最多会松弛 \(m\) 次,堆的大小最多达到 \(m\),所以该算法时间复杂度为 \(O(m \log m)\)。当图为稠密图时,直接暴力更优,时间复杂度为 \(O(n^2)\)

证明:这个算法是正确的当且仅当每次 \(T\) 中取出的最小 \(dis\) 值一定是正确的。归纳假设 \(S\) 里的 \(p_{1},p_{2},\dots,p_{k-1}\) 里的 \(dis\) 一定是正确的。记 \(p_{k}\) 为在 \(T\)\(dis\) 最小的点。如果存在一条经过了 \(T\) 中的点的路径的长度比 \(S\) 扩展而来的 \(dis_{p_{k}}\) 更小。

\(dis_{A} \le dis(p_{i})+w(p_{i},A)+w(B,C)+\dots+w(D,p_{k})<dis_{p_{k}}\),所以 \(dis_{A} < dis_{p_{k}}\),与 \(dis_{p_{k}}\)\(T\) 中最小矛盾。故而从 \(S\) 中扩展而来的最小的 \(dis_{p_{k}}\) 的值一定是正确的。算法成立。

差分约束算法

问题:给定若干形如 $x_{a}−x_{b} \le c $ 或 \(x_{a}−x_{b} \ge c\) 的不等式限制,求任意一组解 \(x_{i}\)

容易发现在最短路算法结束后,一定满足 \(dis_{x}+w \ge dis_{y}\)。因此可以建图后跑最短路,得出的 \(dis\) 就是组合法的解。初始时,\(dis_{i}=0\) 并将所有点入队。

如果存在负环,说明要满足 \(X+a \ge X\),而 \(a<0\),说明此时无解。

由于存在负权边,故使用 SPFA 求解最短路。

Johnson

johnson 算法适用于带负权图的全源最短路。

为解决 dijkstra 无法用于带负权图的问题,对边 \(u \to v\) 的边权 \(w\) 修改为 \(w+h_{u}-h_{v}\)

这样操作后,对于路径 \(S \to T\),设其原来路径边权和为 \(D\),那么修改后为 \(D+h_{S}-h_{T}\),只与 \(S\)\(T\) 有关。

因此可以对修改后的图跑最短路,再将答案减去 \(h_{S}-h_{T}\) 即可。

为满足 \(w + h_{u} - h_{v} \ge 0\),先使用差分约束算法求得 \(h\) 即可。

时间复杂度 \(O(nm \log m)\)

Floyd

Floyd 算法适用于带负权图的全源最短路。(保证无负环)。

\(dis_{k,s,t}\) 表示 \(s \to t\) 的路径中只经过 \([1,k]\) 的点(\(s\)\(t\) 除外)的最短路。

考虑 \(k \leftarrow k+1\),由于 \(k+1\) 一定只会在最短路中出现一次,因此 \(dis_{k+1,s,t} \leftarrow dis_{k,s,k+1}+dis_{k,k+1,t}\) 即可。

去掉第一维导致的重复更新不影响答案。时间复杂度 \(O(n^3)\)

posted @ 2024-09-07 23:27  小超手123  阅读(23)  评论(0)    收藏  举报