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 

题解

  1. 由于Type3类型的边Alice和Bob都可以遍历,所以再构建连通分量的时候应该先将Type3类型的边连通
  2. Type3类型的边连通之后再分别针对与Alice和Bob进行连通Type1和Type2类型的边
    1. 构建两个并查集分别对应Alice与Bob
    2. 对于Alice与Bob两个并查集,连接Type3类型的边。
    3. 对Alice连接Type1类型的边,对Bob连接Type2类型的边
    4. 在连接的过程中如果,当前两个节点已被连通,则表示此时改变可以被删除
  3. 判断结束后alice与bob并查集内是否只能一个连通分量
参考代码
class Solution {
    public int maxNumEdgesToRemove(int n, int[][] edges) {
      int res = 0;

      for(int[] edge : edges) {
        edge[1]--;
        edge[2]--;
      }

      UnionFind alice = new UnionFind(n);
      UnionFind bob = new UnionFind(n);
      for(int[] edge : edges) {
        if(edge[0] == 3) {
          if(alice.union(edge[1], edge[2])) {
            bob.union(edge[1], edge[2]);
          } else {
            res++;
          }
        }
      }

      for(int[] edge : edges) {
        if(edge[0] == 1) {
          if(!alice.union(edge[1], edge[2])) {
            res++;
          } else {
            alice.union(edge[1], edge[2]);
          }
        } else if(edge[0] == 2) {
          if(!bob.union(edge[1], edge[2])) {
            res++;
          } else {
            bob.union(edge[1], edge[2]);
          }
        }
      }

      if(alice.getCount() != 1 && bob.getCount() != 1) return -1;
      return res;

    }

    class UnionFind {
      int[] parent;
      int[] rank;
      int count;

      public UnionFind(int n) {
        parent = new int[n];
        rank = new int[n];
        this.count = n;
        Arrays.fill(rank, 1);
        for(int i = 0; i < n; i++) {
          parent[i] = i;
        }
      }

      public int getCount() {
        return count;
      }

      public boolean union(int a, int b) {
        int rootA = find(a);
        int rootB = find(b);
        if(rootA == rootB) return false;
        if(rank[rootA] < rank[rootB]) {
          parent[rootA] = rootB;
        } else {
          if(rank[rootA] == rank[rootB]) {
            rank[rootA]++;
          }
          parent[rootB] = rootA;
        }
        count--;
        return true;
      }

      public int find(int index) {
        if(index != parent[index]) {
          return find(parent[index]);
        }
        return index;
      }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值