java并查集求解保证图可完全遍历(困难)

总体来说
点我的上一篇文章熟悉一下并查集的应用
首先这题比较常规,不知道为什么会被标注为困难。

提醒,此题一定要注意由于存在两人都能通过的路,
所以在寻找路径的时候首先要从两人共同的路上下手。
因为这样才能够使得多余的路更多的在单人路上,这样
才能删除更多的路径使得最终得到的结果更大。

废话不多的说(尽管已经说很多了),进入正题

首先讲一下我的代码整体思路
我们最终的目标是得到删除最多的边并不使新图失去连通性;
初始化两个并查集Alice和Bob;

  • 当我们遇到前两种类型的边时,分别将判断两人对应的连通性并考虑删除该边。
  • 而遇到第三种类型的边时,同时判断两人的连通性并考虑删除该边。

我们可以这样:
对于新得到的每组边,判断边的类型,如果是1或2就先不管。
对于第三类型的边,判断在加入此条边之前原图是否已经具有连通性,

  • Alice.find(p1)==Alice.find(p2);
  • Bob.find(p1)==Bob.find(p2);
    如果具有连通性,即为以上两式均为真,则表示该第三类型的边是可以删除的。
    我们将最后的待输出结果加1;
    否则,要执行以下命令:
  • Alice.un(p[1],p[2]);
  • Bob.un(p[1],p[2]);
    表示将该第三类型的边加入图中
for(int[] p:edges){
            if(p[0]==3){
                if(a.find(p[1])==a.find(p[2]) && b.find(p[1])==b.find(p[2]))
                    res++;
                else{
                a.un(p[1],p[2]);
                b.un(p[1],p[2]);}
            }
        }

当我们把第三种类型的边都处理完了后再处理前两种类型的边。
并且可以同时处理,因为两种类型相互不影响。

for(int[] p:edges){
            if(p[0]==1){
                if(a.find(p[1])==a.find(p[2]))
                    res++;
                else a.un(p[1],p[2]);
            }
            if(p[0]==2){
                if(b.find(p[1])==b.find(p[2]))
                    res++;
                else b.un(p[1],p[2]);
            }
        }

到此,我们总算是考虑完成了联通的情况,但是对于本身就不连通的图来说,
以上的流程根本判断不出来,所以还得另在外判断。
由于我们使用了并查集,而并查集本身就有判断连通分支的简便方法。
就是,通过对比数组Uf.id,即判断祖宗节点的个数,

  • 如果Uf.id[i]==i为真,则我们就说该节点为祖宗节点。
  • 而对于两条路来说,如果联通则每条路只有一个祖宗节点。
int r1=0,r2=0;
        for(int i=1;i<n+1;i++){
            if(a.id[i]==i)
                r1++;
            if(b.id[i]==i)
                r2++;
        }

据此我么就可以判断原图有没有连通性了。

  • return (r1==r2 && r1==1)?res:-1;

具体的代码提交结果见这里

代码

class Solution {
    class Uf{
        int[] id;
        public Uf(int n){
            id=new int[n+1];
            for(int i=1;i<n+1;i++){
                id[i]=i;
            }
        }
        public int find(int m){
            while(m!=id[m]){
                id[m]=id[id[m]];
                m=id[m];
            }
            return m;
        }
        public void un(int m,int n){
            id[find(m)]=find(n);
        }
    }
    public int maxNumEdgesToRemove(int n, int[][] edges) {
        Uf a=new Uf(n);
        Uf b=new Uf(n);
        int res=0;
        for(int[] p:edges){
            if(p[0]==3){
                if(a.find(p[1])==a.find(p[2]) && b.find(p[1])==b.find(p[2]))
                    res++;
                else{
                a.un(p[1],p[2]);
                b.un(p[1],p[2]);}
            }
        }
        for(int[] p:edges){
            if(p[0]==1){
                if(a.find(p[1])==a.find(p[2]))
                    res++;
                else a.un(p[1],p[2]);
            }
            if(p[0]==2){
                if(b.find(p[1])==b.find(p[2]))
                    res++;
                else b.un(p[1],p[2]);
            }
        }
        int r1=0,r2=0;
        for(int i=1;i<n+1;i++){
            if(a.id[i]==i)
                r1++;
            if(b.id[i]==i)
                r2++;
        }
        return (r1==r2 && r1==1)?res:-1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值