ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

有负权图上的最短路算法 (Goldberg, 1995)

2022-04-09 20:02:49  阅读:360  来源: 互联网

标签:1995 sqrt 负环 算法 Goldberg 操作 反链 负权 我们


最近听说有了一个有负权图上的 \(O(m\log^8 m \log w)\) 算法, 感觉非常厉害, 于是觉得先来研读一个早些的工作. 如果有可能的话再尝试研读新的算法!

我们知道, OI 中常用的在负权图上的 Bellman–Ford 算法可以在 \(O(nm)\) 时间内计算一个有负权图的单源最短路径, 或者确定这张图存在一个负环. 而这篇文章给出了一个 \(O(\sqrt n m \log w)\) 的做法, 其中 \(w\) 是负权边的绝对值上界.

pricing

这是本文涉及的第一个技术, 其实也就是大家熟知的最短路重赋权方法. 如果我们有一个 \(p\) 数组, 满足

\[p(v) \leq p(u) + w(u, v), \]

那么新边权

\[w_p(u, v) = w(u, v) + p(u) - p(v) \]

是非负的, 我们可以使用经典的 Dijkstra 算法求解. 显然在没有负环的时候, 这样的 \(p\) 数组是存在的, 因为最短路数组就满足这个条件.

但是这一转化的另一点好处在于, 我们求出这一 \(p\) 数组的条件是宽了很多的, 具体的设计将在后面看到.

scaling

这是本文涉及的第二个技术, 这个对于学过弱多项式复杂度费用流的同学来说可能比较熟悉了. 对于一张图 \(G=(V,E)\) 上的权值 \(w: E \to \mathbb Z\), 显然将所有边权除以 \(2\) (不考虑取整的时候) 不影响答案和负环的存在性, 然后将权值增加, 不存在负环的图肯定依然不存在负环. 那么我们考虑先对于权值为 \(w' = \lceil w / 2 \rceil\) 的图计算出 \(p\) 数组, 首先如果 \(w\) 里面所有数都是偶数, 那就有 \(w = 2w'\), 自然 \(2p\) 就满足要求, 但实际上有些要将 \(2w'\) 减去 \(1\), 我们就是需要对此进行解决. 我们知道, 根据 \(p\) 数组的定义, 我们必然有 \(d(v) \leq w'(u, v) + d(u)\), 也即 \(w'(u,v) + d(u) - d(v) \geq 0\). 我们考虑重赋权

\[w_p(u, v) = w(u, v) + 2(d(u) - d(v)), \]

则必有 \(w_p(u, v) \geq -1\). 那么我们的目标就是: 通过对 \(p\) 进行适当的调整, 让 \(w_p\geq 0\). 我们将问题变成了 \(\log w\) 轮满足 \(w\geq -1\) 的情况. 那么只要我们设计出一个对此在 \(O(\sqrt n m)\) 时间内调整 price 的方法, 就完成了最初的承诺.

简单算法

我们先把目前 \(w \leq 0\) 的边拿出来, 称其为 \(G_p\). 若 \(G_p\) 的 SCC 里有负边, 那这其实就直接说明了负环的存在性, 我们可以直接报告了. 接下来, 我们考虑缩点后的图. 我们称一个顶点集合是的当每个 \(v\in S\) 都有 \(v\) 通过 \(G_p\) 可达的边都还在 \(S\) 内部, 我们观察如果将这样一个 \(S\) 中的所有点的 \(p\) 值减去 \(1\), 会发生什么.

  • 对于 \(S\) 内部的边, 显然没有影响, 因为两端的 \(p\) 值变化量相同.
  • 对于 \(S\) 连出的边的边权会减少 \(1\), 但是注意到连出的边此时皆为正的, 这不会产生新的 \(-1\) 边, 但有可能会加入新的 \(0\) 边.
  • 对于连入 \(S\) 的边, 边权加 \(1\), 这说明连入 \(S\) 的负边会全部消失, 这是这个操作的关键用途.

如果我们将 \(S\) 取为某个点 \(v\) 通过 \(G_p\) 能到达的所有点 (除去其自身), 那么操作之后, \(v\) 点连出的所有负边都会被解决, 我们只需要 \(O(n)\) 轮就可以解决所有的 \(-1\) 边了!

不幸的是, 每次操作之后, 图的结构会有不小的改变, 因为正权边变小之后会产生一些新的 \(0\) 边, 我们需要继续缩点判负环. 这样的话我们就得到了一个 \(O(nm)\) 的单轮算法.

但事实上, 我们距离最终的算法已相去不远, 这只需要我们用到一点组合观察, 并针对性的设计上述的操作方式.

最终算法

我们记有 \(-1\) 作为出边的点集为 \(K\), 在 \(G_p\) 上的可达性作为偏序, 那么我们考虑能否通过一种精心设计的操作一次性干掉 \(K\) 中的多个点.

反链

对于 \(K\) 中的一条反链 (antichain), 也即这些点两两不能到达, 那么令 \(S\) 为这些点在 \(V\) 上到达的点集之并, 我们对 \(S\) 进行操作, 那么这些点的出边都会被得到解决. 这和上述提到的做法并无不同, 复杂度为 \(O(m)\).

对于 \(K\) 中的一条链 (chain), 也即顶点 \(v_1, v_2, \dots, v_r\) 满足 \(v_i\) 可以通过 \(G_p\) 到达 \(v_{i+1}\), 记 \(S_v\) 是 "\(v\) 直接通过 \(-1\) 边到达的点在 \(G_p\) 中的可达点集之并", 那么我们考虑如果按照 \(i\) 从大到小的顺序操作, 会发生什么.

在还完全没有进行操作的时候, 显然我们是有 \(S_{v_1} \supset S_{v_2} \supset \dots \supset S_{v_r}\) 的, 那么一开始我们对 \(S_{v_r}\) 进行操作, 这会导致有些边的边权发生变化, 有的由正变 \(0\), 也有的由 \(0\) 变正. 更靠前的 \(S_{v_i}\) 有可能变大的同时变小, 但它依然包含现在的 \(S_{v_{i+1}}\), 这是因为它依旧能到达 \(v_{i+1}\), 并且原先 \(v_{i+1}\) 连出的 \(-1\) 边现在是 \(0\), 仍在 \(G_p\) 内部.

但是有没有可能我们操作 \(S\) 之后 \(v_i\) 并没被解决呢? 注意, 这个情况说明 \(S_{v_{i+1}}\) 里面有点可以到达 \(v_i\), 那负环就出现了.

这个操作相对而言维护起来就需要一点技巧了, 需要动态检查哪些边降为了 \(0\), 这可以通过基数排序做到 (这些边一开始的权值不能超过 \(n\), 所以基数排序和值域无关). 这样就做到了 \(O(m)\).

Dilworth

现在你有一个链上的算法, 你有一个反链上的算法. 现在让我们来过最后一关.

由 Dilworth 定理, 链和反链必有一者 \(\geq \sqrt{|K|}\). 因此迭代 \(O(\sqrt {|K|})\) 轮后我们就完成了.

等等, 我们是不是还得够快的找到这么一条链/反链啊?

在 DAG 上 DP 可以求出每个点连出的最长链, 然后对所有极大点构成的反链进行检查即可, 这样即可在 \(O(m)\) 时间内找到.

综上, 一轮总共时间为 \(O(\sqrt n m)\), 算法的总时间为 \(O(\sqrt n m \log w)\).

实现

待实现...

标签:1995,sqrt,负环,算法,Goldberg,操作,反链,负权,我们
来源: https://www.cnblogs.com/Elegia/p/16122335.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有