Complete the MST 题解

这篇博客介绍了如何利用Boruvka算法和Kruskal算法来解决当部分边权未知时的最小生成树问题。文章首先阐述了三种关键的思路:确定边权分配策略、处理边权未知数量大于顶点数的情况以及边权为0的边的处理。随后,博主分别详细解释了当边权未知数量大于n-1和小于等于n-1时的解决方案,并给出了相应的代码实现。最后,讨论了在实际计算中如何避免不必要的操作,以提高算法效率。

一、前言

截至目前,这是练习的第一道完全没有听别人提示的题。hh


二、题解

Hint 1:

首先没有确定边权的边,边权分配一定是一条边的权值为 r e t = v a l 1 ⨁ v a l 2 . . . ⨁ v a l m ret = val_1 \bigoplus val_2 ... \bigoplus val_m ret=val1val2...valm,其余边的边权为 0。
因为这样分配只有未确定边全选的情况才会贡献 ret,而其他分配方式除此情况提供 ret 的贡献,还一定会在一些选择方式中提供贡献,所以这样的分配方式可以以不变应万变。


Hint 2:

如果没有确定边权的边的数量 (number_uncertain) 大于了 n - 1,那么最小生成树中没有确定边权的边的权值一定为 0。
因为生成树的边数为 n - 1,一定有一个没有确定边权的边没有用到,让他的权值为 ret 就行了。


Hint 3:

根据 Kruskal 算法,我们会从边权小的边开始考虑选择,所以我们可以先连边权为 0 的边。


根据上面的三个 Hint,我们可以有如下的解法。

情况1 : n u m b e r _ u n c e r t a i n > n − 1 number\_uncertain > n - 1 number_uncertain>n1

Boruvka 算法。
s[u] 记录以 u 为根的并查集的不能相连的点的编号。
并查集合并时用按秩合并,在较小的并查集里删除不存在于大并查集里的元素。
连通块向外连边时,对于该连通块打一个不可选的编号的表,随便选择一个可选的点,可以考虑每个不可选的编号的相邻编号(因为这种选法和全部遍历选择的存在性是等价的)。
时间复杂度均摊下来是 O ( l o g 2 V ( V + E ) ) O (log_2V (V + E)) O(log2V(V+E))

这样我们将所有的边权为零的边连好了,然后在连好的图上跑一个 Kruskal 就行了。

情况2 : n u m b e r _ u n c e r t a i n ≤ n − 1 number\_uncertain \leq n - 1 number_uncertainn1

这时点的个数不超过 1000 1000 1000 (上界但不是上确界),可以将未确定的边的边权设为 0 跑一个 kruskal,如果未确定的边全部属于 MST 中,那么枚举任意两个点,连接后在形成的环上去掉一个边权为 0 的边(如果没有边权为 0 的边则不能有这样的生成树)。
注意 1: 不要忘了让一个未确定边的边权为 ret 的生成树
注意 2: 实际计算时并不需要连接删边的操作,这样的描述仅便于理解。


代码

### 关于最小生成树问题的编程解决方案 对于解决最小生成树(MST)问题,Prim算法是一个常用的选择。该算法通过逐步构建一棵包含所有顶点的树来找到加权无向图中的最小生成树[^1]。 下面展示了一个简单的Python实现: ```python import sys from collections import defaultdict # 函数用于实现Prim算法求解MST def prim(graph, start): mst = [] # 存储最终的MST边集合 keys = {} # 记录各节点到当前部分MST最短距离 parent = {} # 记录加入MST时由哪个节点引入 # 初始化keys和parent字典 for v in graph: keys[v] = float('inf') parent[v] = None keys[start] = 0 # 设置起始节点的距离为0 visited = set() # 已访问过的节点集 while len(visited) != len(graph): u = min((v for v in graph if v not in visited), key=lambda k: keys[k]) visited.add(u) if parent[u]: mst.append((u, parent[u], keys[u])) for neighbor, weight in graph[u].items(): if neighbor not in visited and weight < keys[neighbor]: keys[neighbor] = weight parent[neighbor] = u return mst graph = { 'A': {'B': 2, 'D': 23}, 'B': {'A': 2, 'C': 9, 'D': 14}, 'C': {'B': 9, 'D': 17, 'E': 6}, 'D': {'A': 23, 'B': 14, 'C': 17, 'E': 8}, 'E': {'C': 6, 'D': 8} } print(prim(graph,'A')) ``` 此代码片段定义了`prim()`函数接收一个表示图形邻接表形式的数据结构以及起点作为参数,并返回构成MST的一系列元组列表[(node1,node2,cost)]。这里采用的是贪心策略,在每一步都选择连接已知区域外最近的一个新节点直到遍历完所有的节点[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值