100244. 带权图里旅途的最小代价
给你一个 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 <= 105
0 <= edges.length <= 105
edges[i].length == 3
0 <= ui, vi <= n - 1
ui != vi
0 <= wi <= 105
1 <= query.length <= 105
query[i].length == 2
0 <= si, ti <= n - 1
原站题解
golang 解法, 执行用时: 231 ms, 内存消耗: 44.4 MB, 提交时间: 2024-04-07 22:28:52
func minimumCost(n int, edges, query [][]int) []int { type edge struct{ to, w int } g := make([][]edge, n) for _, e := range edges { x, y, w := e[0], e[1], e[2] g[x] = append(g[x], edge{y, w}) g[y] = append(g[y], edge{x, w}) } ids := make([]int, n) // 记录每个点所在连通块的编号 for i := range ids { ids[i] = -1 } ccAnd := []int{} // 记录每个连通块的边权的 AND var dfs func(int) int dfs = func(x int) int { ids[x] = len(ccAnd) // 记录每个点所在连通块的编号 and := -1 for _, e := range g[x] { and &= e.w if ids[e.to] < 0 { // 没有访问过 and &= dfs(e.to) } } return and } for i, id := range ids { if id < 0 { // 没有访问过 ccAnd = append(ccAnd, dfs(i)) // 记录每个连通块的边权的 AND } } ans := make([]int, len(query)) for i, q := range query { s, t := q[0], q[1] if s == t { continue } if ids[s] != ids[t] { ans[i] = -1 } else { ans[i] = ccAnd[ids[s]] } } return ans }
golang 解法, 执行用时: 200 ms, 内存消耗: 34.7 MB, 提交时间: 2024-04-07 22:28:40
func minimumCost(n int, edges, query [][]int) []int { fa := make([]int, n) and := make([]int, n) for i := range fa { fa[i] = i and[i] = -1 } var find func(int) int find = func(x int) int { if fa[x] != x { fa[x] = find(fa[x]) } return fa[x] } for _, e := range edges { x, y := find(e[0]), find(e[1]) and[y] &= e[2] if x != y { and[y] &= and[x] fa[x] = y } } ans := make([]int, len(query)) for i, q := range query { s, t := q[0], q[1] if s == t { continue } if find(s) != find(t) { ans[i] = -1 } else { ans[i] = and[find(s)] } } return ans }
python3 解法, 执行用时: 193 ms, 内存消耗: 70.8 MB, 提交时间: 2024-04-07 22:28:27
class Solution: def minimumCost(self, n: int, edges: List[List[int]], query: List[List[int]]) -> List[int]: fa = list(range(n)) and_ = [-1] * n def find(x: int) -> int: if fa[x] != x: fa[x] = find(fa[x]) return fa[x] for x, y, w in edges: x = find(x) y = find(y) and_[y] &= w if x != y: and_[y] &= and_[x] fa[x] = y return [0 if s == t else -1 if find(s) != find(t) else and_[find(s)] for s, t in query]
python3 解法, 执行用时: 249 ms, 内存消耗: 99.4 MB, 提交时间: 2024-04-07 22:28:16
class Solution: def minimumCost(self, n: int, edges: List[List[int]], query: List[List[int]]) -> List[int]: g = [[] for _ in range(n)] for x, y, w in edges: g[x].append((y, w)) g[y].append((x, w)) def dfs(x: int) -> int: and_ = -1 ids[x] = len(cc_and) # 记录每个点所在连通块的编号 for y, w in g[x]: and_ &= w if ids[y] < 0: # 没有访问过 and_ &= dfs(y) return and_ ids = [-1] * n # 记录每个点所在连通块的编号 cc_and = [] # 记录每个连通块的边权的 AND for i in range(n): if ids[i] < 0: cc_and.append(dfs(i)) return [0 if s == t else -1 if ids[s] != ids[t] else cc_and[ids[s]] for s, t in query]
java 解法, 执行用时: 17 ms, 内存消耗: 114.3 MB, 提交时间: 2024-04-07 22:27:54
class Solution { public int[] minimumCost(int n, int[][] edges, int[][] query) { List<int[]>[] g = new ArrayList[n]; Arrays.setAll(g, i -> new ArrayList<>()); for (int[] e : edges) { int x = e[0], y = e[1], w = e[2]; g[x].add(new int[]{y, w}); g[y].add(new int[]{x, w}); } int[] ids = new int[n]; // 记录每个点所在连通块的编号 Arrays.fill(ids, -1); List<Integer> ccAnd = new ArrayList<>(); // 记录每个连通块的边权的 AND for (int i = 0; i < n; i++) { if (ids[i] < 0) { ccAnd.add(dfs(i, ccAnd.size(), g, ids)); } } int[] ans = new int[query.length]; for (int i = 0; i < query.length; i++) { int s = query[i][0], t = query[i][1]; ans[i] = s == t ? 0 : ids[s] != ids[t] ? -1 : ccAnd.get(ids[s]); } return ans; } private int dfs(int x, int curId, List<int[]>[] g, int[] ids) { ids[x] = curId; // 记录每个点所在连通块的编号 int and = -1; for (int[] e : g[x]) { and &= e[1]; if (ids[e[0]] < 0) { // 没有访问过 and &= dfs(e[0], curId, g, ids); } } return and; } }
java 解法, 执行用时: 7 ms, 内存消耗: 104.6 MB, 提交时间: 2024-04-07 22:27:32
class Solution { public int[] minimumCost(int n, int[][] edges, int[][] query) { int[] fa = new int[n]; for (int i = 0; i < n; i++) { fa[i] = i; } int[] and_ = new int[n]; Arrays.fill(and_, -1); for (int[] e : edges) { int x = find(e[0], fa); int y = find(e[1], fa); and_[y] &= e[2]; if (x != y) { and_[y] &= and_[x]; fa[x] = y; } } int[] ans = new int[query.length]; for (int i = 0; i < query.length; i++) { int s = query[i][0], t = query[i][1]; ans[i] = s == t ? 0 : find(s, fa) != find(t, fa) ? -1 : and_[find(s, fa)]; } return ans; } private int find(int x, int[] fa) { if (fa[x] != x) { fa[x] = find(fa[x], fa); } return fa[x]; } }
cpp 解法, 执行用时: 329 ms, 内存消耗: 149.7 MB, 提交时间: 2024-04-07 22:27:10
class Solution { vector<int> fa, and_; int find(int x) { if (fa[x] != x) { fa[x] = find(fa[x]); } return fa[x]; }; public: vector<int> minimumCost(int n, vector<vector<int>> &edges, vector<vector<int>> &query) { fa.resize(n); iota(fa.begin(), fa.end(), 0); and_.resize(n, -1); for (auto &e: edges) { int x = find(e[0]); int y = find(e[1]); and_[y] &= e[2]; if (x != y) { and_[y] &= and_[x]; fa[x] = y; } } vector<int> ans; ans.reserve(query.size()); // 预分配空间 for (auto &q: query) { int s = q[0], t = q[1]; ans.push_back(s == t ? 0 : find(s) != find(t) ? -1 : and_[find(s)]); } return ans; } };
cpp 解法, 执行用时: 374 ms, 内存消耗: 173.1 MB, 提交时间: 2024-04-07 22:27:00
class Solution { vector<vector<pair<int, int>>> g; vector<int> cc_and, ids; int dfs(int x) { ids[x] = cc_and.size(); // 记录每个点所在连通块的编号 int and_ = -1; for (auto &[y, w]: g[x]) { and_ &= w; if (ids[y] < 0) { // 没有访问过 and_ &= dfs(y); } } return and_; } public: vector<int> minimumCost(int n, vector<vector<int>> &edges, vector<vector<int>> &query) { g.resize(n); for (auto &e: edges) { int x = e[0], y = e[1], w = e[2]; g[x].emplace_back(y, w); g[y].emplace_back(x, w); } ids.resize(n, -1); // 记录每个点所在连通块的编号 for (int i = 0; i < n; i++) { if (ids[i] < 0) { // 没有访问过 cc_and.push_back(dfs(i)); // 记录每个连通块的边权的 AND } } vector<int> ans; ans.reserve(query.size()); // 预分配空间 for (auto &q: query) { int s = q[0], t = q[1]; ans.push_back(s == t ? 0 : ids[s] != ids[t] ? -1 : cc_and[ids[s]]); } return ans; } };