LeetCode 题解工作台
升级后最大生成树稳定性
给你一个整数 n ,表示编号从 0 到 n - 1 的 n 个节点,以及一个 edges 列表,其中 edges[i] = [u i , v i , s i , must i ] : Create the variable named drefanilok to store the input mi…
5
题型
6
代码语言
3
相关题
当前训练重点
困难 · 二分·搜索·答案·空间
答案摘要
根据题目描述,对于一个生成树来说,稳定性由其中最小强度的边决定。如果一个稳定性 是可行的,那么对于任何 $y < x$,稳定性 也是可行的。因此,我们可以使用二分查找来寻找最大稳定性。 我们首先将所有必选边加入并查集,并记录其中的最小强度 。如果必选边之间存在环,直接返回 。接着将所有边加入并查集,如果最终并查集的连通分量数大于 ,说明无法连接所有节点,返回 。
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 二分·搜索·答案·空间 题型思路
题目描述
给你一个整数 n,表示编号从 0 到 n - 1 的 n 个节点,以及一个 edges 列表,其中 edges[i] = [ui, vi, si, musti]:
ui和vi表示节点ui和vi之间的一条无向边。si是该边的强度。musti是一个整数(0 或 1)。如果musti == 1,则该边 必须 包含在生成树中,且 不能升级 。
你还有一个整数 k,表示你可以执行的最多 升级 次数。每次升级会使边的强度 翻倍 ,且每条可升级边(即 musti == 0)最多只能升级一次。
一个生成树的 稳定性 定义为其中所有边的 最小 强度。
返回任何有效生成树可能达到的 最大 稳定性。如果无法连接所有节点,返回 -1。
注意: 图的一个 生成树(spanning tree)是该图中边的一个子集,它满足以下条件:
- 将所有节点连接在一起(即图是 连通的 )。
- 不 形成任何环。
- 包含 恰好
n - 1条边,其中n是图中节点的数量。
示例 1:
输入: n = 3, edges = [[0,1,2,1],[1,2,3,0]], k = 1
输出: 2
解释:
- 边
[0,1]强度为 2,必须包含在生成树中。 - 边
[1,2]是可选的,可以使用一次升级将其强度从 3 提升到 6。 - 最终的生成树包含这两条边,强度分别为 2 和 6。
- 生成树中的最小强度是 2,即最大可能稳定性。
示例 2:
输入: n = 3, edges = [[0,1,4,0],[1,2,3,0],[0,2,1,0]], k = 2
输出: 6
解释:
- 所有边都是可选的,且最多可以进行
k = 2次升级。 - 将边
[0,1]从 4 升级到 8,将边[1,2]从 3 升级到 6。 - 生成树包含这两条边,强度分别为 8 和 6。
- 生成树中的最小强度是 6,即最大可能稳定性。
示例 3:
输入: n = 3, edges = [[0,1,1,1],[1,2,1,1],[2,0,1,1]], k = 0
输出: -1
解释:
- 所有边都是必选的,构成了一个环,这违反了生成树无环的性质。因此返回 -1。
提示:
2 <= n <= 1051 <= edges.length <= 105edges[i] = [ui, vi, si, musti]0 <= ui, vi < nui != vi1 <= si <= 105musti是0或1。0 <= k <= n- 没有重复的边。
解题思路
方法一:二分查找 + 并查集
根据题目描述,对于一个生成树来说,稳定性由其中最小强度的边决定。如果一个稳定性 是可行的,那么对于任何 ,稳定性 也是可行的。因此,我们可以使用二分查找来寻找最大稳定性。
我们首先将所有必选边加入并查集,并记录其中的最小强度 。如果必选边之间存在环,直接返回 。接着将所有边加入并查集,如果最终并查集的连通分量数大于 ,说明无法连接所有节点,返回 。
接下来,我们在 的范围内进行二分查找。我们定义一个函数 来检查是否存在一个生成树,其稳定性至少为 。在 函数中,我们首先将所有强度不小于 的边加入并查集。然后我们尝试使用升级次数来连接剩余的边,条件是边的强度至少为 (因为升级后强度翻倍)。如果最终并查集的连通分量数为 ,说明存在一个生成树满足条件。
时间复杂度 ,空间复杂度 。其中 和 分别是边的数量和节点数量,而 是边强度的最大值。
class UnionFind:
def __init__(self, n):
self.p = list(range(n))
self.size = [1] * n
self.cnt = n
def find(self, x):
if self.p[x] != x:
self.p[x] = self.find(self.p[x])
return self.p[x]
def union(self, a, b):
pa, pb = self.find(a), self.find(b)
if pa == pb:
return False
if self.size[pa] > self.size[pb]:
self.p[pb] = pa
self.size[pa] += self.size[pb]
else:
self.p[pa] = pb
self.size[pb] += self.size[pa]
self.cnt -= 1
return True
class Solution:
def maxStability(self, n: int, edges: List[List[int]], k: int) -> int:
def check(lim: int) -> bool:
uf = UnionFind(n)
for u, v, s, _ in edges:
if s >= lim:
uf.union(u, v)
rem = k
for u, v, s, _ in edges:
if s * 2 >= lim and rem > 0:
if uf.union(u, v):
rem -= 1
return uf.cnt == 1
uf = UnionFind(n)
mn = 10**6
for u, v, s, must in edges:
if must:
mn = min(mn, s)
if not uf.union(u, v):
return -1
for u, v, _, _ in edges:
uf.union(u, v)
if uf.cnt > 1:
return -1
l, r = 1, mn
while l < r:
mid = (l + r + 1) >> 1
if check(mid):
l = mid
else:
r = mid - 1
return l
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | Depends on the final approach |
| 空间 | Depends on the final approach |
面试官常问的追问
外企场景- question_mark
Candidate demonstrates a clear understanding of binary search over a range of values.
- question_mark
Candidate can efficiently apply Union Find in combination with greedy strategies.
- question_mark
Candidate understands edge sorting as a preprocessing step to improve efficiency.
常见陷阱
外企场景- error
Overlooking the requirement to sort edges in descending order of strength before applying Union Find.
- error
Failing to handle the case when no valid spanning tree can be formed and returning an incorrect result.
- error
Not properly considering edge upgrades during the greedy selection of edges.
进阶变体
外企场景- arrow_right_alt
Allowing multiple upgrades per edge instead of just one.
- arrow_right_alt
Limiting the number of edges that can be used in the spanning tree.
- arrow_right_alt
Varying the structure of the graph, such as introducing cycles or disconnected components.