zoukankan      html  css  js  c++  java
  • [P3367]【模板】并查集.题解

    这橙题无非只是模板题,套并查集模板AC!并查集的算法思想十分值得大家学习!

    老规矩,先看题目。

    题目告诉我们要对N个元素进行M次操作,对其中的一些进行合并操作,并输出两个集合的合并情况。这里我们首先会想到用并查集!没错,但萌新们不用怕,我接下来讲解一下并查集的思路,你们就明白了!


    思路

    并查集:树形数据结构

    听起来比较简单,实现起来也很简单。我们分3步学会模板!

    首先我们先要学会构建find函数(查找一个结点的根结点)
    拿这题举例,我们通过一个数组f[10010] (开小容易RE或TLE)。如果其祖先等于自己,直接return回去。否则通过递归不断循环,最后找出结点的根结点。
    OK~那我们接下来就看看find函数的定义吧!

    1 int find(int x){
    2     if(f[x]==x) return x;//如果结点本身就是根结点,直接返回自己
    3     else return f[x]=find(f[x]);//否则通过路径压缩递归返回根结点
    4 }

    关于find函数还有个路径压缩的知识点:

     1 else return find(f[x]);//递归返回,一个个找它的父结点,最终找到根节点 

     1 else return f[x]=find(f[x]);//直接将结点连到其根节点上。find定义代码最后一句就是它 

    第二个代码优化时间的方法就叫路径压缩

    我们看两张图就理解了!

     

     如图,求这棵树所有结点的祖先,时间复杂度取决于这棵树的深度,是O(n²)。

     如图,求这棵树所有结点的祖先,时间复杂度取决于结点的个数,是O(n)。
    路径压缩能优化时间复杂度

    接下来来是并,我们合并两个结点的根结点时需要用到查(find函数)!
    那我们就看看join函数的定义吧!

    1 void join(int x,int y){
    2     f[find(x)]=find(y);//f数组用来存结点的祖先,通过find函数,合并两个结点x、y的根结点
    3     return;//合并后别忘了直接返回
    4 }

    最后一步直接判断两个结点所在集合是否有共同根结点。用find函数就可以了!
    那我们再看看判断代码吧!

    1 if(find(a)==find(b))
    2     cout<<"Y"<<endl;//如果结点a和b有相同的根结点,则输出“Y”
    3 else
    4     cout<<"N"<<endl;//否则输出“N”

    好了,我们的思路就讲到这里,下面是AC代码!


    代码

    (注释版)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,f[10010],a,b,c;//数组f[i]记录i的根结点
     4 int find(int x){
     5     if(f[x]==x) return x;//如果结点本身就是根结点,直接返回自己
     6     else return f[x]=find(f[x]);//否则通过路径压缩递归返回根结点
     7 }
     8 void join(int x,int y){
     9     f[find(x)]=find(y);//通过find函数,合并两个结点x、y的根结点
    10     return;//合并后别忘了直接返回
    11 }
    12 int main(){//好习惯,直接从主函数读起
    13     cin>>n>>m;
    14     for(int i=1;i<=n;i++)
    15         f[i]=i;//初始化根结点数组,确保每个结点现在的根结点都是自己
    16     for(int i=1;i<=m;i++){
    17         cin>>a>>b>>c;
    18         if(a==1)//a为1时进行合并操作
    19             join(b,c);//直接调用join()函数
    20         else
    21             if(find(b)==find(c))//当结点b、c的根结点相等时,则它们在一个集合里
    22                 cout<<"Y"<<endl;
    23             else//否则它们不属于同一个集合
    24                 cout<<"N"<<endl;
    25     }
    26     return 0;
    27 }

    (无注释版)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,f[10010],a,b,c;
     4 int find(int x){
     5     if(f[x]==x) return x;
     6     else return f[x]=find(f[x]);
     7 }
     8 void join(int x,int y){
     9     f[find(x)]=find(y);
    10     return;
    11 }
    12 int main(){
    13     cin>>n>>m;
    14     for(int i=1;i<=n;i++)
    15         f[i]=i;
    16     for(int i=1;i<=m;i++){
    17         cin>>a>>b>>c;
    18         if(a==1)
    19             join(b,c);
    20         else
    21             if(find(b)==find(c))
    22                 cout<<"Y"<<endl;
    23             else
    24                 cout<<"N"<<endl;
    25     }
    26     return 0;
    27 }

    总结

    并查集分3步:

    1. 把每个结点所在的集合初始化为它自己
    2. 将两个不同集合的结点合并(join函数)
    3. 查找结点所在集合的根结点(find函数)

  • 相关阅读:
    PHP is_numeric 检测变量是否为数字或数字字符串
    CSS texttransform实现首个或全部字母大写或小写
    To be a true man
    前辈的话
    做好你自己
    PHP mysql_real_escape_string() 函数
    这些事,我们早就该知道……
    Win7 如何更改用户名
    js或css文件后面跟参数的原因说明
    网页优化插件 YSlow
  • 原文地址:https://www.cnblogs.com/integricode26/p/P3367-union-find-disjoint-sets-template-solution.html
Copyright © 2011-2022 走看看