LeetCode 题解工作台
带权图里旅途的最小代价
给你一个 n 个节点的带权无向图,节点编号为 0 到 n - 1 。 给你一个整数 n 和一个数组 edges ,其中 edges[i] = [u i , v i , w i ] 表示节点 u i 和 v i 之间有一条权值为 w i 的无向边。 在图中,一趟旅途包含一系列节点和边。旅途开始和结束点…
4
题型
5
代码语言
3
相关题
当前训练重点
困难 · 并查集
答案摘要
我们注意到,一个正整数与其他若干个正整数不断进行“按位与”运算,结果只会越来越小。因此,为了使得旅途的代价尽可能小,我们应该将处于同一个连通分量的所有边的权值进行“按位与”运算,然后再进行查询。 那么,问题转化为,如何找出同一个连通份量的所有边,然后进行“按位与”运算。
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 并查集 题型思路
题目描述
给你一个 n 个节点的带权无向图,节点编号为 0 到 n - 1 。
给你一个整数 n 和一个数组 edges ,其中 edges[i] = [ui, vi, wi] 表示节点 ui 和 vi 之间有一条权值为 wi 的无向边。
在图中,一趟旅途包含一系列节点和边。旅途开始和结束点都是图中的节点,且图中存在连接旅途中相邻节点的边。注意,一趟旅途可能访问同一条边或者同一个节点多次。
如果旅途开始于节点 u ,结束于节点 v ,我们定义这一趟旅途的 代价 是经过的边权按位与 AND 的结果。换句话说,如果经过的边对应的边权为 w0, w1, w2, ..., wk ,那么代价为w0 & w1 & w2 & ... & wk ,其中 & 表示按位与 AND 操作。
给你一个二维数组 query ,其中 query[i] = [si, ti] 。对于每一个查询,你需要找出从节点开始 si ,在节点 ti 处结束的旅途的最小代价。如果不存在这样的旅途,答案为 -1 。
返回数组 answer ,其中 answer[i] 表示对于查询 i 的 最小 旅途代价。
示例 1:
输入:n = 5, edges = [[0,1,7],[1,3,7],[1,2,1]], query = [[0,3],[3,4]]
输出:[1,-1]
解释:

第一个查询想要得到代价为 1 的旅途,我们依次访问:0->1(边权为 7 )1->2 (边权为 1 )2->1(边权为 1 )1->3 (边权为 7 )。
第二个查询中,无法从节点 3 到节点 4 ,所以答案为 -1 。
示例 2:
输入:n = 3, edges = [[0,2,7],[0,1,15],[1,2,6],[1,2,1]], query = [[1,2]]
输出:[0]
解释:

第一个查询想要得到代价为 0 的旅途,我们依次访问:1->2(边权为 1 ),2->1(边权 为 6 ),1->2(边权为 1 )。
提示:
1 <= n <= 1050 <= edges.length <= 105edges[i].length == 30 <= ui, vi <= n - 1ui != vi0 <= wi <= 1051 <= query.length <= 105query[i].length == 20 <= si, ti <= n - 1
解题思路
方法一:贪心 + 并查集
我们注意到,一个正整数与其他若干个正整数不断进行“按位与”运算,结果只会越来越小。因此,为了使得旅途的代价尽可能小,我们应该将处于同一个连通分量的所有边的权值进行“按位与”运算,然后再进行查询。
那么,问题转化为,如何找出同一个连通份量的所有边,然后进行“按位与”运算。
我们可以用并查集来维护连通分量。
具体地,我们遍历每一条边 ,将 和 进行合并。然后,我们再一次遍历每一条边 ,找到 和 所在的连通分量的根节点 ,用一个数组 记录每个连通分量的所有边的权值进行“按位与”运算的结果。
最后,对于每一个查询 ,我们首先判断 与 是否相等,如果相等,那么答案为 ,否则,我们判断 和 是否在同一个连通分量中,如果在同一个连通分量中,那么答案为该查询的连通分量的根节点的 值,否则,答案为 。
时间复杂度 ,空间复杂度 。其中 , 和 分别表示节点数、边数和查询数,而 表示 Ackermann 函数的反函数。
class UnionFind:
def __init__(self, n):
self.p = list(range(n))
self.size = [1] * 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]
return True
class Solution:
def minimumCost(
self, n: int, edges: List[List[int]], query: List[List[int]]
) -> List[int]:
g = [-1] * n
uf = UnionFind(n)
for u, v, _ in edges:
uf.union(u, v)
for u, _, w in edges:
root = uf.find(u)
g[root] &= w
def f(u: int, v: int) -> int:
if u == v:
return 0
a, b = uf.find(u), uf.find(v)
return g[a] if a == b else -1
return [f(s, t) for s, t in query]
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | complexity is O(m + n + q) because each edge and vertex is processed once during DSU merging and bit mask updates, and each query is answered in constant time using precomputed structures. Space complexity is O(n + m) for storing DSU arrays and bit masks. |
| 空间 | O(n + m) |
面试官常问的追问
外企场景- question_mark
Look for usage of Disjoint Set Union with array indices to represent connected components.
- question_mark
Expect candidates to optimize repeated path queries with bit manipulation for fast cost computation.
- question_mark
Watch for correct handling of walks that can revisit vertices or edges multiple times.
常见陷阱
外企场景- error
Assuming a walk cannot reuse edges or vertices, leading to incorrect minimum costs.
- error
Ignoring cases where nodes are disconnected, producing wrong outputs instead of -1.
- error
Overcomplicating propagation without using array plus bit manipulation, causing timeouts on large inputs.
进阶变体
外企场景- arrow_right_alt
Compute minimum cost walks in directed weighted graphs with array plus bit manipulation for strongly connected components.
- arrow_right_alt
Limit walk lengths to k edges and use array-based DP with bit masks to find minimum cost.
- arrow_right_alt
Handle dynamic edge insertions or deletions while maintaining minimum cost queries using DSU and bit manipulation.