题意:
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
输入: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 。
解题思路:
变种的最小生成树(克鲁斯卡尔算法),贪心可得优先选择类型3的边,因为类型3的边Alice和Bob都可以访问,然后再选择类型1或类型2,查看最后两个两个连通块是否大于1,如果大于1,不满足
class Solution {
public:
vector<int> pa, pb;
int get(vector<int>& p, int x) { // 路径压缩进行查找
if(p[x] != x) {
p[x] = get(p, p[x]);
}
return p[x];
}
int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
pa.resize(n+1), pb.resize(n+1);
for(int i = 1; i <= n; i++) { // 初始化两个连通块
pa[i] = pb[i] = i;
}
int res = 0, ca = n, cb = n;
for(auto&e: edges) {
if(e[0] == 3) { // 优先选择 类型3
int x = e[1], y = e[2];
int pax = get(pa, x), pay = get(pa, y); 此时,Alice和Bob都可以通过
int pbx = get(pb, x), pby = get(pb, y);
if(pax != pay) {
pa[pax] = pay; ca--;
pb[pbx] = pby; cb--;
} else {
res++; // 如果相等,说明两点已经联通,多出一条边
}
}
}
for(auto &e: edges) {
int t = e[0], x = e[1], y = e[2];
if(t == 1) {
int pax = get(pa, x), pay = get(pa, y);
if(pax != pay) {
pa[pax] = pay; ca--;
} else {
res++;
}
} else if(t == 2){
int pbx = get(pb, x), pby = get(pb, y);
if(pbx != pby) {
pb[pbx] = pby; cb--;
} else {
res++;
}
}
}
if(ca > 1 || cb > 1) {
return -1;
} else {
return res;
}
}
};