zoukankan      html  css  js  c++  java
  • 【模板】并查集

    并查集

    并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。——以上摘自360百科。

    并查集并查集,字面意思来看完成的也主要就是三个操作:合并,查询,集合。

    其主要操作,就是为了判断某两个元素是否在同一个集合里面,而一个一个的寻找枚举由太过复杂,于是便有了并查集这个方便的思想:寻找祖宗。若两个元素的祖宗节点是相同的,那么这两个元素也必定属于同一个集合。

    首先来看一个例题:

    题目背景

    若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

    题目描述

    规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

    输入输出格式

    输入格式:

     

    第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。

    以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。

    接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

    输出格式:

    P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

    begin::那么这个显然这就是一个十分经典的并查集模板题了。

    若是不用并查集,而是按照传统的思路来看,首先必须要保存m条边,然后再次进行普通的遍历手法,时间复杂度大约在O(n^2)左右,效率很低。

    由此想到了运用并查集:

    首先便是对于并查集的第一项工作:怎么实现连通块??

    将每个人抽象成为一个点,编号分别为1,2,3,.....n,运用集合的思路,对于每个人建立一个集合,此时每一个集合里都是只有这个人本身,也可以理解为:自己就是自己的祖宗。

    之后每次初选一个亲戚关系时,就将他们合并起来,如果之后又询问,就在当前结果中看两个元素是否处于同一个集合就可以了。

    那么接下来就是第二个问题:如何判断两个集合是否有亲戚关系??

    这就是之前所说的:找祖宗,然后看两个集合的祖宗是不是相同就可以了。

    下面附上代码::

    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #define MAXN 1000001
    using namespace std;
    int f[MAXN];//指的是某一位的父亲节点 
    int n,m;//n:总人数 m:已知关系数量 
    int x,y;//亲戚双方 
    int q;//询问次数 
    int i;//用于循环 
    int find(int x)//寻找x的祖宗 递归版 (普通版)
    {
        if(f[x]!=x)
            return find(f[x]);
        else 
            return f[x];    
    } 
    int find_2(int x)//递归版 (普通版) 
    {
        while(f[x]!=x)
            x=f[x];
        return x;
    }
    int find_imp(int x)//寻找x的祖宗(路径压缩改进版) (递归版)
    {
        if(f[x]!=x)
            return f[x]=find_imp(f[x]);//路径压缩,改进的核心 
        else return f[x];
    }
    int find_imp_2(int x)//寻找x的祖宗(路径压缩改进版) (非递归版)
    {
        while(f[x]!=x)
            x=f[x];
        return x;
    }
    void unionn(int r1,int r2)//合并 r1,r2
    {
        f[r2]=r1;
    }
    int judge(int x,int y)//判断x y是否处于同一集合
    {
        int Auto1=find_imp(x);
        int Auto2=find_imp(y);
        if(Auto1==Auto2)
            return true;
        else
            return false;
     } 
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            f[i]=i;//每个点的父亲是其本身,即该集合中只有其本身一个
        for(int i=1;i<=m;i++)
        {
            cin>>x>>y;
            int r1=find_imp(x);
            int r2=find_imp(y);
            if(r1!=r2)
                unionn(x,y);
         } 
         cin>>q;
         for(int i=1;i<=q;i++)
         {
             cin>>x>>y;
             if(judge(x,y)==1)
             cout<<"Yes";
             else cout<<"No";
         }
         return 0;
    }
  • 相关阅读:
    MySQL中 INSERT INTO 和 SELECT 的组合使用
    Chrome浏览器如何强制刷新
    html中点击a链接不跳转
    PHP strstr() 和 strrchr() 详解
    Mac 在当前目录打开终端
    PHP 函数相关
    ARM、DSP、FPGA的技术特点和区别
    ARM版本系列及家族成员梳理
    【重磅推荐】嵌入式Linux经典书单(部分含视频)
    Makefile的引入及规则
  • 原文地址:https://www.cnblogs.com/sue_shallow/p/8506175.html
Copyright © 2011-2022 走看看