保证图可完全遍历

A l i c e Alice Alice B o b Bob Bob 共有一个无向图,其中包含 n n n 个节点和 3 3 3 种类型的边:

  • 类型 1:只能由 Alice 遍历。
  • 类型 2:只能由 Bob 遍历。
  • 类型 3:Alice 和 Bob 都可以遍历。

给你一个数组 e d g e s edges edges ,其中 e d g e s [ i ] = [ t y p e i , u i , v i ] edges[i] = [type_i, u_i, v_i] edges[i]=[typei,ui,vi] 表示节点 u i u_i ui v i v_i vi 之间存在类型为 t y p e i type_i typei 的双向边。请你在保证图仍能够被 A l i c e Alice Alice B o b Bob Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始, A l i c e Alice Alice B o b Bob Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。

返回可以删除的最大边数,如果 A l i c e Alice Alice B o b Bob Bob 无法完全遍历图,则返回 − 1 -1 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,2] [1,1,2] [ 1 , 1 , 3 ] [1,1,3] [1,1,3] 这两条边, A l i c e Alice Alice B o b Bob Bob 仍然可以完全遍历这个图。再删除任何其他的边都无法保证图可以完全遍历。所以可以删除的最大边数是 2 2 2
示例 2:

输入:n = 4, edges = [[3,1,2],[3,2,3],[1,1,4],[2,1,4]]
输出:0
解释:注意,删除任何一条边都会使 A l i c e Alice Alice B o b Bob Bob 无法完全遍历这个图。
示例 3:

输入:n = 4, edges = [[3,2,3],[1,1,2],[2,3,4]]
输出:-1
解释:在当前图中, A l i c e Alice Alice 无法从其他节点到达节点 4 4 4 。类似地, B o b Bob Bob 也不能达到节点 1 1 1 。因此,图无法完全遍历。

提示:

1 < = n < = 1 0 5 1 < = e d g e s . l e n g t h < = m i n ( 1 0 5 , 3 ∗ n ∗ ( n − 1 ) / 2 ) e d g e s [ i ] . l e n g t h = = 3 1 < = e d g e s [ i ] [ 0 ] < = 3 1 < = e d g e s [ i ] [ 1 ] < e d g e s [ i ] [ 2 ] < = n 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 1<=n<=1051<=edges.length<=min(105,3n(n1)/2)edges[i].length==31<=edges[i][0]<=31<=edges[i][1]<edges[i][2]<=n
所有元组 ( t y p e i , u i , v i ) (type_i, u_i, v_i) (typei,ui,vi) 互不相同

  1. 思路
    我们称类型 1 , 2 , 3 1, 2, 3 1,2,3 的边分别为「Alice 独占边」「Bob 独占边」以及「公共边」。
    首先我们需要思考什么样的图是可以被 A l i c e Alice Alice B o b Bob Bob 完全遍历的。对于 Alice 而言,她可以经过的边是「Alice 独占边」以及「公共边」,由于她需要能够从任意节点到达任意节点,那么就说明:

当图中仅有「Alice 独占边」以及「公共边」时,整个图是连通的,即整个图只包含一个连通分量。

同理,对于 Bob 而言,当图中仅有「Bob 独占边」以及「公共边」时,整个图也要是连通的。

由于题目描述中希望我们删除最多数目的边,这等价于保留最少数目的边。换句话说,我们可以从一个仅包含 nn 个节点(而没有边)的无向图开始,逐步添加边,使得满足上述的要求。

那么我们应该按照什么策略来添加边呢?直觉告诉我们,「公共边」的重要性大于「Alice 独占边」以及「Bob 独占边」,因为「公共边」是 Alice 和 Bob 都可以使用的,而他们各自的独占边却不能给对方使用。「公共边」的重要性也是可以证明的:

对于一条连接了两个不同的连通分量的「公共边」而言,如果我们不保留这条公共边,那么 Alice 和 Bob
就无法往返这两个连通分量,即他们分别需要使用各自的独占边。因此,Alice 需要一条连接这两个连通分量的独占边,Bob
同样也需要一条连接这两个连通分量的独占边,那么一共需要两条边,这就严格不优于直接使用一条连接这两个连通分量的「公共边」了。

因此,我们可以遵从优先添加「公共边」的策略。具体地,我们遍历每一条「公共边」,对于其连接的的两个节点:

如果这两个节点在同一个连通分量中,那么添加这条「公共边」是无意义的;

如果这两个节点不在同一个连通分量中,我们就可以(并且一定)添加这条「公共边」,然后合并这两个节点所在的连通分量。

这就提示了我们使用并查集来维护整个图的连通性,上述的策略只需要用到并查集的「查询」和「合并」这两个最基础的操作。

在处理完了所有的「公共边」之后,我们需要处理他们各自的独占边,而方法也与添加「公共边」类似。我们将当前的并查集复制一份,一份交给 Alice,一份交给 Bob。随后 Alice 不断地向并查集中添加「Alice 独占边」,Bob 不断地向并查集中添加「Bob 独占边」。在处理完了所有的独占边之后,如果这两个并查集都只包含一个连通分量,那么就说明 Alice 和 Bob 都可以遍历整个无向图。

  1. 代码
class Solution {
public:

    int find(vector<int>&f,int x){
        return x==f[x]?x:f[x]=find(f,f[x]);
    }

    int union1(vector<int>&f,int x,int y){
        x=find(f,x);
        y=find(f,y);
        if (x==y) return 1;
        f[x]=y;
        return 0;
    }

    int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
        vector<int>f(n+1,0);
        for (int i=0;i<=n;i++){
            f[i]=i;
        }
        int res=0;
        for (auto &it:edges){
            if (it[0]==3){
                res+=union1(f,it[1],it[2]);
            }
        }
        vector<int>f1=f;
        for (auto &it:edges){
            if (it[0]==1){
                res+=union1(f1,it[1],it[2]);
            }
        }
        vector<int>f2=f;
        for (auto &it:edges){
            if (it[0]==2){
                res+=union1(f2,it[1],it[2]);
            }
        }
        int x=find(f1,1),y=find(f2,1);
        for (int i=1;i<=n;i++){
            if (find(f1,i)!=x) return -1;
            if (find(f2,i)!=y) return -1;
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值