UNPKG

oi-wiki

Version:
265 lines (163 loc) 7.97 kB
## 定义 (还记得这些定义吗?在阅读下列内容之前,请务必了解 [图论基础](/graph/basic) 部分。) - 路径 - 最短路 - 有向图中的最短路、无向图中的最短路 - 单源最短路、每对结点之间的最短路 ## 性质 对于边权为正的图,任意两个结点之间的最短路,不会经过重复的结点。 对于边权为正的图,任意两个结点之间的最短路,不会经过重复的边。 对于边权为正的图,任意两个结点之间的最短路,任意一条的结点数不会超过 $n$,边数不会超过 $n-1$。 ## Floyd 算法 是用来求任意两个结点之间的最短路的。 复杂度比较高,但是常数小,容易实现。(我会说只有三个 `for` 吗?) 适用于任何图,不管有向无向,边权正负,但是最短路必须存在。(不能有个负环) ### 实现 我们定义一个数组 `f[k][x][y]`,表示只允许经过结点 $1$ 到 $k$,结点 $x$ 到结点 $y$ 的最短路长度。 很显然,`f[n][x][y]` 就是结点 $x$ 到结点 $y$ 的最短路长度。 我们来考虑怎么求这个数组 `f[0][x][y]`:边权,或者 $0$,或者 $+\infty$ `f[0][x][x]` 什么时候应该是 $+\infty$?`f[k][x][y] = min(f[k-1][x][y], f[k-1][x][k]+f[k-1][k][y])` 上面两行都显然是对的,然而这个做法空间是 $O(N^3)$。 但我们发现数组的第一维是没有用的,于是可以直接改成 `f[x][y] = min(f[x][y], f[x][k]+f[k][y])````c++ for (k = 1; k <= n; k++) { for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { f[i][j] = min(f[i][j], f[i][k] + f[k][j]); } } } ``` 时间复杂度是 $O(N^3)$,空间复杂度是 $O(N^2)$。 ### 应用 ???+ question "给一个正权无向图,找一个最小权值和的环" 首先这一定是一个简单环。 想一想这个环是怎么构成的。 考虑环上编号最大的结点 u。 `f[u-1][x][y]` 和 (u,x), (u,y)共同构成了环。 在Floyd的过程中枚举u,计算这个和的最小值即可。 $O(n^3)$。 ???+question "已知一个有向图中任意两点之间是否有连边,要求判断任意两点是否联通" 该问题即是求**图的传递闭包**。 我们只需要按照 Floyd 的过程,逐个加入点判断一下。 只是此时的边的边权变为 $1/0$, 而取 $\min$ 变成了**与**运算。 再进一步用 bitset 优化,复杂度可以到 $O(\frac{n^3}{w})$。 ```c++ //std::bitset<SIZE> f[SIZE]; for (k = 1; k <= n; k++) for (i = 1; i <= n; i++) if(f[i][k]) f[i] = f[i] & f[k]; ``` * * * ## Bellman-Ford 算法 一种基于松弛(relax)操作的最短路算法。 支持负权。 能找到某个结点出发到所有结点的最短路,或者报告某些最短路不存在。 在国内 OI 界,你可能听说过的 “SPFA”,就是 Bellman-Ford 算法的一种实现。(优化) ### 实现 假设结点为 $S$。 先定义 $dist(u)$ 为 $S$ 到 $u$ (当前)的最短路径长度。 $relax(u,v)$: $dist(v) = min(dist(v), dist(u) + edge\_len(u, v))$ $relax$ 是从哪里来的呢? 三角形不等式: $dist(v) \leq dist(u) + edge\_len(u, v)$。 证明:反证法,如果不满足,那么可以用 $relax$ 操作来更新 $dist(v)$ 的值。 Bellman-Ford 算法如下: ```text while (1) for each edge(u, v) relax(u, v); ``` 当一次循环中没有 $relax$ 操作成功时停止。 每次循环是 $O(m)$ 的,那么最多会循环多少次呢? 答案是 $\infty$!(如果有一个 $S$ 能走到的负环就会这样) 但是此时某些结点的最短路不存在。 我们考虑最短路存在的时候。 由于一次 $relax$ 会使(被 $relax$ 的)最短路的边数至少 $+1$,而最短路的边数最多为 $n-1$。 所以最多(连续)$relax$ $n-1$ 次……($relax$ 一定是环环相扣的,不然之前就能被 $relax$ 掉) 所以最多循环 $n-1$ 次。 总时间复杂度 $O(NM)$。 **(对于最短路存在的图)** ```text relax(u, v) { dist[v] = min(dist[v], dist[u] + edge_len(u, v)); } for (i = 1; i <= n; i++) { dist[i] = edge_len(S, i); } for (i = 1; i < n; i++) { for each edge(u, v) { relax(u, v); } } ``` 注:这里的 $edge\_len(u, v)$ 表示边的权值,如果该边不存在则为 $+\infty$,$u=v$ 则为 $0$。 ### 应用 给一张有向图,问是否存在负权环。 做法很简单,跑 Bellman-Ford 算法,如果有个点被 $relax$ 成功了 $n$ 次,那么就一定存在。 如果 $n-1$ 次之内算法结束了,就一定不存在。 ### 优化 SPFA Shortest Path Faster Algorithm 很多时候我们并不需要那么多无用的 $relax$ 操作。 很显然,只有上一次被 $relax$ 的结点,所连接的边,才有可能引起下一次的 $relax$。 那么我们用队列来维护 “哪些结点可能会引起 $relax$”,就能只访问必要的边了。 ```text q = new queue(); q.push(S); in_queue[S] = true; while (!q.empty()) { u = q.pop(); in_queue[u] = false; for each edge(u, v) { if (relax(u, v) && !in_queue[v]) { q.push(v); in_queue[v] = true; } } } ``` SPFA 的时间复杂度为 $O(kM)~ (k\approx 2)$ (玄学),但 **理论上界** 为 $O(NM)$,精心设计的稠密图可以随便卡掉 SPFA,所以考试时谨慎使用 (NOI 2018 中很多选手的 SPFA 被卡掉了)。 #### SPFA 的优化之 SLF Small Label First 即在新元素加入队列时,如果队首元素权值大于新元素权值,那么就把新元素加入队首,否则依然加入队尾。 该优化在确实在一些图上有显著效果,其复杂度也有保证,但是如果有负权边的话,可以直接卡到指数级。 * * * ## Dijkstra 算法 Dijkstra 是个人名(荷兰姓氏)。 IPA: /ˈdikstrɑ/ 或 /ˈdɛikstrɑ/。 这种算法只适用于非负权图,但是时间复杂度非常优秀。 也是用来求单源最短路径的算法。 ### 实现 主要思想是,将结点分成两个集合:已确定最短路长度的,未确定的。 一开始第一个集合里只有 $S$。 然后重复这些操作: (1)$relax$ 那些刚刚被加入第一个集合的结点的所有出边。 (2)从第二个集合中,选取一个最短路长度最小的结点,移到第一个集合中。 直到第二个集合为空,算法结束。 时间复杂度:只用分析集合操作,$n$ 次 delete-min,$m$ 次 decrease-key。 如果用暴力: $O(n^2 + m)$。 如果用堆:$O((n+m) \log m)$。 如果用线段树(ZKW 线段树):$(O(n+m)\log n)$ 如果用 Fibonacci 堆: $O(n \log n + m)$(这就是为啥优秀了)。 等等,还没说正确性呢! 分两步证明:先证明任何时候第一个集合中的元素的 $dist$ 一定不大于第二个集合中的。 再证明第一个集合中的元素的最短路已经确定。 第一步,一开始时成立(基础),在每一步中,加入集合的元素一定是最大值,且是另一边最小值,$relax$ 又是加上非负数,所以仍然成立。(归纳) (利用非负权值的性质) 第二步,考虑每次加进来的结点,到他的最短路,上一步必然是第一个集合中的元素(否则他不会是第二个集合中的最小值,而且有第一步的性质),又因为第一个集合已经全部 $relax$ 过了,所以最短路显然确定了。 ```text H = new heap(); H.insert(S, 0); dist[S] = 0; for (i = 1; i <= n; i++) { u = H.delete_min(); for each edge(u, v) { if (relax(u, v)) { H.decrease_key(v, dist[v]); } } } ``` * * * ## 不同方法的比较 | Floyd | Bellman-Ford | Dijkstra | | ---------- | ------------ | ---------------- | | 每对结点之间的最短路 | 单源最短路 | 单源最短路 | | 没有负环的图 | 任意图 | 非负权图 | | $O(N^3)$ | $O(NM)$ | $O((N+M)\log M)$ |