1579. 保证图可完全遍历
Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:
给你一个数组 edges
,其中 edges[i] = [typei, ui, vi]
表示节点 ui
和 vi
之间存在类型为 typei
的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。
返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。
示例 1:
输入:n = 4, edges = [[3,1,2],[3,2,3],[1,1,3],[1,2,4],[1,1,2],[2,3,4]] 输出:2 解释:如果删除 [1,1,2] 和 [1,1,3] 这两条边,Alice 和 Bob 仍然可以完全遍历这个图。再删除任何其他的边都无法保证图可以完全遍历。所以可以删除的最大边数是 2 。
示例 2:
输入:n = 4, edges = [[3,1,2],[3,2,3],[1,1,4],[2,1,4]] 输出:0 解释:注意,删除任何一条边都会使 Alice 和 Bob 无法完全遍历这个图。
示例 3:
输入:n = 4, edges = [[3,2,3],[1,1,2],[2,3,4]] 输出:-1 解释:在当前图中,Alice 无法从其他节点到达节点 4 。类似地,Bob 也不能达到节点 1 。因此,图无法完全遍历。
提示:
1 <= n <= 10^5
1 <= edges.length <= min(10^5, 3 * n * (n-1) / 2)
edges[i].length == 3
1 <= edges[i][0] <= 3
1 <= edges[i][1] < edges[i][2] <= n
(typei, ui, vi)
互不相同原站题解
golang 解法, 执行用时: 308 ms, 内存消耗: 19.9 MB, 提交时间: 2023-09-21 10:36:05
type unionFind struct { parent, size []int setCount int // 当前连通分量数目 } func newUnionFind(n int) *unionFind { parent := make([]int, n) size := make([]int, n) for i := range parent { parent[i] = i size[i] = 1 } return &unionFind{parent, size, n} } func (uf *unionFind) find(x int) int { if uf.parent[x] != x { uf.parent[x] = uf.find(uf.parent[x]) } return uf.parent[x] } func (uf *unionFind) union(x, y int) bool { fx, fy := uf.find(x), uf.find(y) if fx == fy { return false } if uf.size[fx] < uf.size[fy] { fx, fy = fy, fx } uf.size[fx] += uf.size[fy] uf.parent[fy] = fx uf.setCount-- return true } func (uf *unionFind) inSameSet(x, y int) bool { return uf.find(x) == uf.find(y) } func maxNumEdgesToRemove(n int, edges [][]int) int { ans := len(edges) alice, bob := newUnionFind(n), newUnionFind(n) for _, e := range edges { x, y := e[1]-1, e[2]-1 if e[0] == 3 && (!alice.inSameSet(x, y) || !bob.inSameSet(x, y)) { // 保留这条公共边 alice.union(x, y) bob.union(x, y) ans-- } } uf := [2]*unionFind{alice, bob} for _, e := range edges { if tp := e[0]; tp < 3 && uf[tp-1].union(e[1]-1, e[2]-1) { // 保留这条独占边 ans-- } } if alice.setCount > 1 || bob.setCount > 1 { return -1 } return ans }
javascript 解法, 执行用时: 224 ms, 内存消耗: 72.6 MB, 提交时间: 2023-09-21 10:35:51
/** * @param {number} n * @param {number[][]} edges * @return {number} */ // 并查集模板 class UnionFind { constructor (n) { this.parent = new Array(n).fill(0).map((element, index) => index); this.size = new Array(n).fill(1); // 当前连通分量数目 this.setCount = n; } findset (x) { if (this.parent[x] === x) { return x; } this.parent[x] = this.findset(this.parent[x]); return this.parent[x]; } unite (a, b) { let x = this.findset(a), y = this.findset(b); if (x === y) { return false; } if (this.size[x] < this.size[y]) { [x, y] = [y, x]; } this.parent[y] = x; this.size[x] += this.size[y]; this.setCount -= 1; return true; } connected (a, b) { const x = this.findset(a), y = this.findset(b); return x === y; } } var maxNumEdgesToRemove = function(n, edges) { const ufa = new UnionFind(n), ufb = new UnionFind(n); let ans = 0; // 节点编号改为从 0 开始 for (const edge of edges) { edge[1] -= 1; edge[2] -= 1; } // 公共边 for (const [t, u, v] of edges) { if (t === 3) { if (!ufa.unite(u, v)) { ans += 1; } else { ufb.unite(u, v); } } } // 独占边 for (const [t, u, v] of edges) { if (t === 1) { // Alice 独占边 if (!ufa.unite(u, v)) { ans += 1; } } else if (t === 2) { // Bob 独占边 if (!ufb.unite(u, v)) { ans += 1; } } } if (ufa.setCount !== 1 || ufb.setCount !== 1) { return -1; } return ans; };
python3 解法, 执行用时: 396 ms, 内存消耗: 53.4 MB, 提交时间: 2023-09-21 10:35:36
# 并查集模板 class UnionFind: def __init__(self, n: int): self.parent = list(range(n)) self.size = [1] * n self.n = n # 当前连通分量数目 self.setCount = n def findset(self, x: int) -> int: if self.parent[x] == x: return x self.parent[x] = self.findset(self.parent[x]) return self.parent[x] def unite(self, x: int, y: int) -> bool: x, y = self.findset(x), self.findset(y) if x == y: return False if self.size[x] < self.size[y]: x, y = y, x self.parent[y] = x self.size[x] += self.size[y] self.setCount -= 1 return True def connected(self, x: int, y: int) -> bool: x, y = self.findset(x), self.findset(y) return x == y class Solution: def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int: ufa, ufb = UnionFind(n), UnionFind(n) ans = 0 # 节点编号改为从 0 开始 for edge in edges: edge[1] -= 1 edge[2] -= 1 # 公共边 for t, u, v in edges: if t == 3: if not ufa.unite(u, v): ans += 1 else: ufb.unite(u, v) # 独占边 for t, u, v in edges: if t == 1: # Alice 独占边 if not ufa.unite(u, v): ans += 1 elif t == 2: # Bob 独占边 if not ufb.unite(u, v): ans += 1 if ufa.setCount != 1 or ufb.setCount != 1: return -1 return ans
java 解法, 执行用时: 13 ms, 内存消耗: 107.8 MB, 提交时间: 2023-09-21 10:35:21
class Solution { public int maxNumEdgesToRemove(int n, int[][] edges) { UnionFind ufa = new UnionFind(n); UnionFind ufb = new UnionFind(n); int ans = 0; // 节点编号改为从 0 开始 for (int[] edge : edges) { --edge[1]; --edge[2]; } // 公共边 for (int[] edge : edges) { if (edge[0] == 3) { if (!ufa.unite(edge[1], edge[2])) { ++ans; } else { ufb.unite(edge[1], edge[2]); } } } // 独占边 for (int[] edge : edges) { if (edge[0] == 1) { // Alice 独占边 if (!ufa.unite(edge[1], edge[2])) { ++ans; } } else if (edge[0] == 2) { // Bob 独占边 if (!ufb.unite(edge[1], edge[2])) { ++ans; } } } if (ufa.setCount != 1 || ufb.setCount != 1) { return -1; } return ans; } } // 并查集模板 class UnionFind { int[] parent; int[] size; int n; // 当前连通分量数目 int setCount; public UnionFind(int n) { this.n = n; this.setCount = n; this.parent = new int[n]; this.size = new int[n]; Arrays.fill(size, 1); for (int i = 0; i < n; ++i) { parent[i] = i; } } public int findset(int x) { return parent[x] == x ? x : (parent[x] = findset(parent[x])); } public boolean unite(int x, int y) { x = findset(x); y = findset(y); if (x == y) { return false; } if (size[x] < size[y]) { int temp = x; x = y; y = temp; } parent[y] = x; size[x] += size[y]; --setCount; return true; } public boolean connected(int x, int y) { x = findset(x); y = findset(y); return x == y; } }
cpp 解法, 执行用时: 412 ms, 内存消耗: 136 MB, 提交时间: 2023-09-21 10:35:03
// 并查集模板 class UnionFind { public: vector<int> parent; vector<int> size; int n; // 当前连通分量数目 int setCount; public: UnionFind(int _n): n(_n), setCount(_n), parent(_n), size(_n, 1) { iota(parent.begin(), parent.end(), 0); } int findset(int x) { return parent[x] == x ? x : parent[x] = findset(parent[x]); } bool unite(int x, int y) { x = findset(x); y = findset(y); if (x == y) { return false; } if (size[x] < size[y]) { swap(x, y); } parent[y] = x; size[x] += size[y]; --setCount; return true; } bool connected(int x, int y) { x = findset(x); y = findset(y); return x == y; } }; class Solution { public: int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) { UnionFind ufa(n), ufb(n); int ans = 0; // 节点编号改为从 0 开始 for (auto& edge: edges) { --edge[1]; --edge[2]; } // 公共边 for (const auto& edge: edges) { if (edge[0] == 3) { if (!ufa.unite(edge[1], edge[2])) { ++ans; } else { ufb.unite(edge[1], edge[2]); } } } // 独占边 for (const auto& edge: edges) { if (edge[0] == 1) { // Alice 独占边 if (!ufa.unite(edge[1], edge[2])) { ++ans; } } else if (edge[0] == 2) { // Bob 独占边 if (!ufb.unite(edge[1], edge[2])) { ++ans; } } } if (ufa.setCount != 1 || ufb.setCount != 1) { return -1; } return ans; } };
java 解法, 执行用时: 15 ms, 内存消耗: 81.2 MB, 提交时间: 2023-09-21 10:34:19
class Solution { public int maxNumEdgesToRemove(int n, int[][] edges) { int count = 0; UnionFind aliceUf = new UnionFind(n); UnionFind bobUf = new UnionFind(n); // 先连接所有的公共边 for (int[] edge : edges) { if (edge[0] == 3) { if (aliceUf.isConnected(edge[1], edge[2])) { count++; } else { aliceUf.union(edge[1], edge[2]); bobUf.union(edge[1], edge[2]); } } } // 再连接Alice边和Bob边 for (int[] edge : edges) { if (edge[0] == 1) { if (aliceUf.isConnected(edge[1], edge[2])) count++; else aliceUf.union(edge[1], edge[2]); } if (edge[0] == 2) { if (bobUf.isConnected(edge[1], edge[2])) count++; else bobUf.union(edge[1], edge[2]); } } return aliceUf.getSize() * bobUf.getSize() > 1 ? -1 : count; } static class UnionFind { private final int[] father; private int size; public UnionFind(int n) { this.father = new int[n + 1]; for (int i = 0; i <= n; i++) father[i] = i; this.size = n; } public int find(int i) { if (father[i] == i) return i; else { father[i] = find(father[i]); return father[i]; } } public void union(int i, int j) { father[find(i)] = find(j); this.size -= 1; } public boolean isConnected(int i, int j) { return find(i) == find(j); } public int getSize() { return size; } } }
python3 解法, 执行用时: 432 ms, 内存消耗: 54.3 MB, 提交时间: 2023-09-21 10:34:03
# 借助并查集按照人物遍历每一条边 class UnionFind: def __init__(self,size): self.father = [None] * (size+1) self.num_of_sets = size def find(self,x): if self.father[x] is None: return x self.father[x] = self.find(self.father[x]) return self.father[x] def is_connected(self,x,y): return self.find(x) == self.find(y) def merge(self,x,y): self.father[self.find(x)] = self.find(y) self.num_of_sets -= 1 class Solution: def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int: res = 0 uf_alice = UnionFind(n) uf_bob = UnionFind(n) for _type,n1,n2 in edges: if _type == 3: if not uf_alice.is_connected(n1,n2): uf_alice.merge(n1,n2) uf_bob.merge(n1,n2) else: res += 1 for _type,n1,n2 in edges: if _type == 1: if not uf_alice.is_connected(n1,n2): uf_alice.merge(n1,n2) else: res += 1 if _type == 2: if not uf_bob.is_connected(n1,n2): uf_bob.merge(n1,n2) else: res += 1 return -1 if uf_alice.num_of_sets * uf_bob.num_of_sets > 1 else res