并查集

Posted lcez56jsy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集相关的知识,希望对你有一定的参考价值。

 大家都有亲戚朋友对不对。

  来让我们看两道题

  亲戚    朋友 

  显然我们需要并查集。

 So,什么是并查集?

  并,就是合并关系(也就是认祖宗)。查,就是查找关系(就是看祖宗是不是一个人)。集,是因为它是个集合。

  并查集怎么写呢?

  前面说过要认祖宗,我们就有了一个father[i],来记录每个i的祖宗。(以下简写为fa[i])。

  当数据给出两个人i,j的关系时,我们就把i和j并起来,即让j认i为祖宗。(反过来也行)。

  要查找时,就判断两人的祖宗是否相同就行了。我们还缺一步:找爹。

怎么实现呢?看代码吧

//初始化
int fa[10001];
int main()
{cin>>n;
     for(int i=1;i<=n;i++)
       fa[i]=i;//一开始所有人的爹都是自己
}
//找爹
int find(int x)
{if(fa[x]==x)return x;//如果爹是自己,就返回自己
  return find(fa[x]);//如果不是,就一直找
}
//合并
void unionn(int x,int y)
{x=find(x);y=find(y);
  fa[y]=fa[x];//这里让y的爹认x的爹为爹效果和让y认x当爹是一样的
}

这里的find会被毒瘤数据卡到超时,所以我们有个优化

int find(int x)
{if(fa[x]!=x)fa[x]=find(fa[x]);//这样优化后x的爹,爷爷...都认了根节点的祖宗了
  return fa[x];
}

好了并查集讲完了233

我们回到开头那两道题

亲戚比较友善(毕竟是亲戚),只要改下输入输出就行了。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
int n,m,p,fa[5001];
int find(int x)
{
    if(fa[x]!=x)fa[x]=find(fa[x]);
    return fa[x];
}
void unionn(int x,int y)
{ int xx=find(x),yy=find(y);
   fa[yy]=xx;
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=n;i++)
     fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(find(x)!=find(y))
         unionn(x,y);     
    }
   for(int i=1;i<=p;i++)
   {int x,y;
     scanf("%d%d",&x,&y);
     if(find(x)==find(y))
      printf("Yes\\n");
     else printf("No\\n");//千万别手残都敲成大写字母!!!
   }
}

朋友这道题,其实是道语文题

我们看下样例

技术图片

发现一个规律:男生和女生中,编号对应且和小明/小红认识的人可以配成一对

但是事实并不是这样(我会告诉你我在这卡了n遍?)

事实是只要认识小明和小红,就能配成一对

跳过坑,就是题解

//因为女生是负数,所以我们把女生编号取负后放入另一个数组里
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int faman[20001],fawo[20001];//fawo[i]就是女生的数组
int n,m,p,q;
int findm(int x)
{if(faman[x]!=x)faman[x]=findm(faman[x]);
  return faman[x];
}
int findw(int x)
{if(fawo[x]!=x)fawo[x]=findw(fawo[x]);
  return fawo[x];
}
void um(int x,int y)
{    x=faman[x];y=faman[y];
    faman[y]=x;
}
void uw(int x,int y)
{    x=fawo[x];y=fawo[y];
    fawo[y]=x;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=n;i++)
     {faman[i]=i;
     }
     for(int i=1;i<=m;i++)
      fawo[i]=i;
    for(int i=1;i<=p;i++)
    {int x,y;
      scanf("%d%d",&x,&y);
      if(findm(x)!=findm(y))
       um(x,y);
    }

    for(int i=1;i<=q;i++)
    {int x,y;
      scanf("%d%d",&x,&y);
      x=-x;y=-y;//因为这里女生是负数,所以要取负
      if(findw(x)!=findw(y))
       uw(x,y);
    }
    int ansm=0,answ=0;
    int k=max(n,m);
    for(int i=1;i<=n;i++)
    {
       if(findm(i)==findm(1))ansm++;
     
    } 
    for(int i=1;i<=m;i++)
    {  if(findw(i)==findw(1))
       {answ++;
       }
    }
    printf("%d",min(ansm,answ));//取小明/小红认识的人中最少的(你不能让两男或者两女在一起,更不能让多余的人自己在一起,这是规定)
}

 

 

 

  

以上是关于并查集的主要内容,如果未能解决你的问题,请参考以下文章

想要学会并查集吗?看我四十行代码实现它

树--12---并查集

笔记并查集---无向图处理代码模板及类型题

并查集

力扣 每日一题 886. 可能的二分法难度:中等,rating: 1794(并查集 / 拆点优化的扩展域并查集)

并查集