|
| 1 | + |
| 2 | +## 题目地址(3108. 带权图里旅途的最小代价 - 力扣(LeetCode)) |
| 3 | + |
| 4 | +https://leetcode.cn/problems/minimum-cost-walk-in-weighted-graph/ |
| 5 | + |
| 6 | +## 题目描述 |
| 7 | + |
| 8 | +<p>给你一个 <code>n</code> 个节点的带权无向图,节点编号为 <code>0</code> 到 <code>n - 1</code> 。</p> |
| 9 | + |
| 10 | +<p>给你一个整数 <code>n</code> 和一个数组 <code>edges</code> ,其中 <code>edges[i] = [u<sub>i</sub>, v<sub>i</sub>, w<sub>i</sub>]</code> 表示节点 <code>u<sub>i</sub></code> 和 <code>v<sub>i</sub></code> 之间有一条权值为 <code>w<sub>i</sub></code> 的无向边。</p> |
| 11 | + |
| 12 | +<p>在图中,一趟旅途包含一系列节点和边。旅途开始和结束点都是图中的节点,且图中存在连接旅途中相邻节点的边。注意,一趟旅途可能访问同一条边或者同一个节点多次。</p> |
| 13 | + |
| 14 | +<p>如果旅途开始于节点 <code>u</code> ,结束于节点 <code>v</code> ,我们定义这一趟旅途的 <strong>代价</strong> 是经过的边权按位与 <code>AND</code> 的结果。换句话说,如果经过的边对应的边权为 <code>w<sub>0</sub>, w<sub>1</sub>, w<sub>2</sub>, ..., w<sub>k</sub></code> ,那么代价为<code>w<sub>0</sub> & w<sub>1</sub> & w<sub>2</sub> & ... & w<sub>k</sub></code> ,其中 <code>&</code> 表示按位与 <code>AND</code> 操作。</p> |
| 15 | + |
| 16 | +<p>给你一个二维数组 <code>query</code> ,其中 <code>query[i] = [s<sub>i</sub>, t<sub>i</sub>]</code> 。对于每一个查询,你需要找出从节点开始 <code>s<sub>i</sub></code> ,在节点 <code>t<sub>i</sub></code> 处结束的旅途的最小代价。如果不存在这样的旅途,答案为 <code>-1</code> 。</p> |
| 17 | + |
| 18 | +<p>返回数组<em> </em><code>answer</code> ,其中<em> </em><code>answer[i]</code><em> </em>表示对于查询 <code>i</code> 的 <strong>最小</strong> 旅途代价。</p> |
| 19 | + |
| 20 | +<p> </p> |
| 21 | + |
| 22 | +<p><strong class="example">示例 1:</strong></p> |
| 23 | + |
| 24 | +<div class="example-block"> |
| 25 | +<p><span class="example-io"><b>输入:</b>n = 5, edges = [[0,1,7],[1,3,7],[1,2,1]], query = [[0,3],[3,4]]</span></p> |
| 26 | + |
| 27 | +<p><span class="example-io"><b>输出:</b>[1,-1]</span></p> |
| 28 | + |
| 29 | +<p><strong>解释:</strong></p> |
| 30 | + |
| 31 | +<p><img alt="" src="https://assets.leetcode.com/uploads/2024/01/31/q4_example1-1.png" style="padding: 10px; background: rgb(255, 255, 255); border-radius: 0.5rem; width: 351px; height: 141px;"></p> |
| 32 | + |
| 33 | +<p>第一个查询想要得到代价为 1 的旅途,我们依次访问:<code>0->1</code>(边权为 7 )<code>1->2</code> (边权为 1 )<code>2->1</code>(边权为 1 )<code>1->3</code> (边权为 7 )。</p> |
| 34 | + |
| 35 | +<p>第二个查询中,无法从节点 3 到节点 4 ,所以答案为 -1 。</p> |
| 36 | + |
| 37 | +<p><strong class="example">示例 2:</strong></p> |
| 38 | +</div> |
| 39 | + |
| 40 | +<div class="example-block"> |
| 41 | +<p><span class="example-io"><b>输入:</b>n = 3, edges = [[0,2,7],[0,1,15],[1,2,6],[1,2,1]], query = [[1,2]]</span></p> |
| 42 | + |
| 43 | +<p><span class="example-io"><b>输出:</b>[0]</span></p> |
| 44 | + |
| 45 | +<p><strong>解释:</strong></p> |
| 46 | + |
| 47 | +<p><img alt="" src="https://assets.leetcode.com/uploads/2024/01/31/q4_example2e.png" style="padding: 10px; background: rgb(255, 255, 255); border-radius: 0.5rem; width: 211px; height: 181px;"></p> |
| 48 | + |
| 49 | +<p>第一个查询想要得到代价为 0 的旅途,我们依次访问:<code>1->2</code>(边权为 1 ),<code>2->1</code>(边权 为 6 ),<code>1->2</code>(边权为 1 )。</p> |
| 50 | +</div> |
| 51 | + |
| 52 | +<p> </p> |
| 53 | + |
| 54 | +<p><strong>提示:</strong></p> |
| 55 | + |
| 56 | +<ul> |
| 57 | + <li><code>1 <= n <= 10<sup>5</sup></code></li> |
| 58 | + <li><code>0 <= edges.length <= 10<sup>5</sup></code></li> |
| 59 | + <li><code>edges[i].length == 3</code></li> |
| 60 | + <li><code>0 <= u<sub>i</sub>, v<sub>i</sub> <= n - 1</code></li> |
| 61 | + <li><code>u<sub>i</sub> != v<sub>i</sub></code></li> |
| 62 | + <li><code>0 <= w<sub>i</sub> <= 10<sup>5</sup></code></li> |
| 63 | + <li><code>1 <= query.length <= 10<sup>5</sup></code></li> |
| 64 | + <li><code>query[i].length == 2</code></li> |
| 65 | + <li><code>0 <= s<sub>i</sub>, t<sub>i</sub> <= n - 1</code></li> |
| 66 | +</ul> |
| 67 | + |
| 68 | + |
| 69 | +## 前置知识 |
| 70 | + |
| 71 | +- |
| 72 | + |
| 73 | +## 公司 |
| 74 | + |
| 75 | +- 暂无 |
| 76 | + |
| 77 | +## 思路 |
| 78 | + |
| 79 | +由于代价是按位与 ,而不是相加,因此如果 s 到 t 我们尽可能多的走,那么 and 的值就会越来越小。这是因为两个数的与一定不比这两个数大。 |
| 80 | + |
| 81 | +- 考虑如果 s 不能到达 t,那么直接返回 -1。 |
| 82 | +- 如果 s 到 t 可以到达,说明 s 和 t 在同一个联通集。对于联通集外的点,我们无法到达。而对于联通集内的点,我们可以到达。前面说了,我们尽可能多的做,因此对于联通集内的点,我们都走一遍。答案就是联通集合中的边的 and 值。 |
| 83 | + |
| 84 | +使用并查集模板可以解决,主要改动点在于 `union` 方法。大家可以对照我的并查集标准模板看看有什么不同。 |
| 85 | + |
| 86 | +## 关键点 |
| 87 | + |
| 88 | +- |
| 89 | + |
| 90 | +## 代码 |
| 91 | + |
| 92 | +- 语言支持:Python3 |
| 93 | + |
| 94 | +Python3 Code: |
| 95 | + |
| 96 | +```python |
| 97 | + |
| 98 | + |
| 99 | +class UF: |
| 100 | + def __init__(self, M): |
| 101 | + self.parent = {} |
| 102 | + self.cnt = 0 |
| 103 | + self.all_and = {} |
| 104 | + # 初始化 parent,size 和 cnt |
| 105 | + # Initialize parent, size and cnt |
| 106 | + for i in range(M): |
| 107 | + self.parent[i] = i |
| 108 | + self.cnt += 1 |
| 109 | + self.all_and[i] = 2 ** 30 - 1 # 也可以初始化为 -1 |
| 110 | + |
| 111 | + def find(self, x): |
| 112 | + if x != self.parent[x]: |
| 113 | + self.parent[x] = self.find(self.parent[x]) |
| 114 | + return self.parent[x] |
| 115 | + return x |
| 116 | + def union(self, p, q, w): |
| 117 | + # if self.connected(p, q): return # 这道题对于联通的情况不能直接 return,具体可以参考示例 2. 环的存在 |
| 118 | + leader_p = self.find(p) |
| 119 | + leader_q = self.find(q) |
| 120 | + self.parent[leader_p] = leader_q |
| 121 | + # p 连通块的 and 值为 w1,q 连通块的 and 值为 w2,合并后就是 w1 & w2 & w |
| 122 | + self.all_and[leader_p] = self.all_and[leader_q] = self.all_and[leader_p] & w & self.all_and[leader_q] |
| 123 | + self.cnt -= 1 |
| 124 | + def connected(self, p, q): |
| 125 | + return self.find(p) == self.find(q) |
| 126 | + |
| 127 | +class Solution: |
| 128 | + def minimumCost(self, n: int, edges: List[List[int]], query: List[List[int]]) -> List[int]: |
| 129 | + g = [[] for _ in range(n)] |
| 130 | + uf = UF(n) |
| 131 | + for x, y, w in edges: |
| 132 | + g[x].append((y, w)) |
| 133 | + g[y].append((x, w)) |
| 134 | + uf.union(x, y, w) |
| 135 | + |
| 136 | + ans = [] |
| 137 | + for s, t in query: |
| 138 | + if not uf.connected(s, t): |
| 139 | + ans.append(-1) |
| 140 | + else: |
| 141 | + ans.append(uf.all_and[uf.parent[s]]) |
| 142 | + return ans |
| 143 | + |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | +``` |
| 148 | + |
| 149 | + |
| 150 | +**复杂度分析** |
| 151 | + |
| 152 | +令 m 为 edges 长度。 |
| 153 | + |
| 154 | +- 时间复杂度:$O(m + n)$ |
| 155 | +- 空间复杂度:$O(m + n)$ |
| 156 | + |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 161 | +
|
| 162 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 163 | + |
| 164 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 165 | + |
| 166 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 167 | + |
| 168 | + |
0 commit comments