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) 互不相同

 

解决方案

并查集方法。首先是 Alice 和 Bob,判断是否能覆盖所有节点,不能则返回-1. 之后,清空数据,只计算3类型的边,记录最多能访问多少个节点count。这些节点组成一个可用公共边互通的子集,子集内遍历访问的边数就是count-1; 再找下一个未访问的节点,并继续形成子集,并把所有的子集边数相加为sumBian = sum(count[i] - 1); 这个sum则是Alice和Bob遍历中,能公用的最大边数。 则Alice和Bob 访问使用的边数量为: common = (n - 1 - sum) + (n - 1 - sum)  + sum 最大可删除的边为: edges.length - common = edges.length - (n - 1) * 2 + sum.

class Solution {
    int set_num;
    int set_num_1, set_num_2, set_num_3 = 0;//集合总数
    public int maxNumEdgesToRemove(int n, int[][] edges) {   
        int[] father = new int[n+1];  
        ArrayList<Integer> set_label_1 = new ArrayList<>();
        ArrayList<Integer> set_label_2 = new ArrayList<>();
        ArrayList<Integer> set_label_3 = new ArrayList<>();
        // Alice
        father = init(n);
        for(int i=0; i<edges.length; i++){
            if((edges[i][0] == 1) || (edges[i][0] == 3)){
                set_num = join(edges[i][1], edges[i][2], set_num, father);
            }
        }
        if(set_num != 1){
            System.out.println("Alice");
            return -1;
        }
        // Bob
        father = init(n);
        for(int i=0; i<edges.length; i++){
            if((edges[i][0] == 2) || (edges[i][0] == 3)){
                set_num = join(edges[i][1], edges[i][2], set_num, father);
            }
        }
        if(set_num != 1){
            System.out.println("Bob");
            return -1;
        }
        // calculate 3rd edges;
        father = init(n);
        for(int i=0; i<edges.length; i++){
            if(edges[i][0] == 3){
                set_num = join(edges[i][1], edges[i][2], set_num, father);
                set_num_3 = set_num_3 + 1; 
                // 前两步是判定是否返回-1,在当前已经联通的情况下 先计算3类型的互通的边的数量sum
                // 其次alice 和bob连通分别需要的边的数量为n-1,那么最终实际需要的边的数量 (n - 1 -sum) + (n - 1 - sum) + sum (两倍的剪掉在加上)
            }
        }
        int extra_3 = set_num_3 - (n-set_num); //3类型中总计需要n-set_num条3类型的边, 现有set_num_3条边,多余set_num_3 - (n-set_num)条边, 实际需要(n-set_num)
        int sum = n-set_num; //实际需要(n-set_num)
        int res = edges.length - ((n - 1 - sum)* 2) - sum;
        // res = res - extra_3;
        return res;
    }
    public int join(int x,int y, int set_num, int[] father){
        int x_root = getRoot(x, father);
        int y_root = getRoot(y, father);
        if (x_root != y_root){
            father[x_root] = y_root;
            set_num = set_num - 1;
        }
        return set_num;
    }
    public int getRoot(int x, int[] father){
        int root = x;
        while(father[root] != root){
            root = father[root];
        }
        while(father[x] != root){
            int temp = father[x];
            father[x] = root;
            x = temp;
        }
        return root;
    }
    public int[] init(int n){
        int[] father = new int[n+1];
        set_num = n;
        for(int i=1;i<=n;i++){
            father[i] = i;
        }
        return father;
    }
    

}

Tips: 1.前两步是判定是否返回-1,在当前已经联通的情况下 先计算3类型的互通的边的数量sum

         2.其次alice 和bob连通分别需要的边的数量为n-1,那么最终实际需要的边的数量 (n - 1 -sum) + (n - 1 - sum) + sum (两倍的剪掉在加上)

        计算sum:3类型中总计需要n-set_num条3类型的边, 现有set_num_3条边,多余set_num_3 - (n-set_num)条边, 实际需要(n-set_num)

        int sum = n-set_num; //实际需要(n-set_num)

        int res = edges.length - ((n - 1 - sum)* 2) - sum;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值