1579. 保证图可完全遍历
Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:
- 类型 1:只能由 Alice 遍历。
- 类型 2:只能由 Bob 遍历。
- 类型 3:Alice 和 Bob 都可以遍历。
给你一个数组 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)
互不相同
方法一:贪心 + 并查集
解题思路
依然是连通性的问题,此题的基础是保证图中的所有点对于两个人分别是连通的(并查集),在保证连通性的前提下尽量少使用边(贪心)。
- 贪心:因为有「类型 3」的边存在,应该优先使用「类型 3」的边。
- 并查集:使用两个并查集
ufAlice
和ufBob
分别维护 Alice 和 Bob 的点的连通性。 - 处理多余的边:遍历
edges
时,有 edges[i] 对应的两个点已经连通时,说明这是一条多余边
参考代码
public int maxNumEdgesToRemove(int n, int[][] edges) {
UnionFind ufAlice = new UnionFind(n);
UnionFind ufBob = new UnionFind(n);
int ret = 0;
// 图中的点是从角标 1 开始,转为从角标 0 开始
// 优先使用类型 3 的边
for (int[] edge : edges) {
edge[1]--;
edge[2]--;
if (edge[0] == 3) {
if (ufAlice.union(edge[1], edge[2])) {
ufBob.union(edge[1], edge[2]);
} else {
ret++;
}
}
}
// 使用类型 1 和类型 2 的边
for (int[] edge : edges) {
if (edge[0] == 1) {
if (!ufAlice.union(edge[1], edge[2])) {
ret++;
}
} else if(edge[0] == 2) {
if (!ufBob.union(edge[1], edge[2])){
ret++;
}
}
}
return (ufAlice.getCount() == 1 && ufBob.getCount() == 1) ? ret : -1;
}
class UnionFind {
private int[] parent;
private int count;
public UnionFind(int n) {
count = n;
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public boolean union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) {
return false;
}
count--;
parent[rootX] = rootY;
return true;
}
public int find(int x) {
return parent[x] == x ? parent[x] : (parent[x] = find(parent[x]));
}
public int getCount() {
return count;
}
}
执行结果