algorithm - 给出边的最小排列权重,使得给定的边集是最小生成树

标签 algorithm minimum-spanning-tree disjoint-sets

问题:

给定一个包含 N 个节点和 M 条边的图,边的索引从 1 -> M。保证任意 2 个节点之间存在路径。

您需要为 M 条边分配权重。 权重在[1...M]范围内,每个数字只能出现一次。

简而言之,答案应该是 [1...M] 的排列数组,其中 arr[i] = x 表示 edge[i] 的权重为 x。

给定一个包含 n-1 条边的集合 R。 R 保证是图的生成树。

找到一种分配权重的方法,使 R 是图的最小生成树,如果有多个答案,则打印具有最小词典顺序的那个。

约束:

N, M <= 10^6

示例:

边缘:

3 4
1 2
2 3
1 3
1 4

R = [2, 4, 5]

example image

Answer: 3 4 5 1 2

解释:

如果像上图一样为图分配权重,则 MST 将是集合 R,并且它具有最小的字典顺序。

我对 O(N^2) 的看法:

因为它要求最小字典顺序,所以我遍历边列表,按递增顺序分配权重。最初,w = 1。可以有 3 种情况:

  • 如果edge[i]在R中,赋值weight[i] = w,w增加1
  • 如果 edge[i] 不在 R 中:假设 edge[i] 连接节点 u 和 v。为 R 中从 u 到 v 的路径中的每条边分配权重并增加 w(如果该边尚未分配) .然后为edge[i]分配权重并增加w
  • 如果 edge[i] 被赋值,则跳过它

有什么方法可以改进我的解决方案,使其可以在 O(N.logN) 或更短的时间内运行?

最佳答案

是的,有一个时间复杂度为 O(m log m) 的算法。

非树边e基本循环e和树中端点之间的路径组成>e。给定权重,生成树最小当且仅当对于每个非树边 e,e 的基本循环中最重的边是 e 本身。

词典编目目标适用于贪婪算法,在该算法中,我们为边 1 找到最无效的分配,然后在给定边 1 的情况下找到边 2,然后在给定之前的边的情况下找到边 3,等等。核心思想如下:如果下一个未分配的边为非树边,在其基本循环中将下一个编号分配给未分配的树边;然后分配下一个数字。

在示例中,第 3-4 边是第一个,第 1-3 和 1-4 边完成其基本循环。因此,我们分配 1-3 → ​​1 和 1-4 → 2,然后是 3-4 → 3。接下来是 1-2,一个树边,所以 1-2 → 4。最后,2-3 → ​​5 (1-2和 1-3 已经分配)。

为了有效地实现这一点,我们需要两个要素:一种枚举基本循环中未分配边的方法,以及一种分配数字的方法。我对前者的建议是存储生成树,并收缩指定的边。我们不需要任何花哨的东西;首先在某处生成生成树并运行深度优先搜索以记录父指针和深度。 e 的基本循环将由到 e 端点的最不公共(public)祖先的路径给出。为了进行收缩,我们添加了一个 bool 字段来指示父边是否收缩,然后使用来自不相交集森林的路径压缩技巧。这项工作将是 O(m log m) 最坏情况,但 O(m) 平均情况。我认为很有可能可以在此处插入离线最不常见祖先算法,以将最坏情况降低到 O(m)。

至于数字分配,我们可以在线性时间内处理。对于每条边,记录导致它被分配的边的索引。最后,通过该索引进行稳定的桶排序,通过将树边放在非树之前来打破平局。这可以在 O(m) 时间内完成。

关于algorithm - 给出边的最小排列权重,使得给定的边集是最小生成树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70530290/

相关文章:

algorithm - 连接二维数组中的不相交集

algorithm - 射气球并收集最高分

php - 压缩大量文件以生成统计文件

algorithm - 具有多个根顶点的图中的最小生成树

algorithm - 网格上具有最小转数的生成树

c++ - 什么时候拥有太多动态数组会很危险?

algorithm - 使用 union find(也称为不相交集)检测图是否为二分图

algorithm - 删除直接连接的组件后的最小元素数

c++ - 消除标点符号和空格

java - 使用 Prim 算法在迷宫中放置房间