并查集入门

Posted nishikino-curtis

tags:

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

并查集的基础概念及实现

部分内容引用自wikipedia.org

  • 并查集(Union-Find Sets)是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题,定义了两个用于此数据结构的操作:
  • Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
  • Union:将两个子集合并成同一个集合。
  • 在实际应用中,常随着find操作进行路径压缩,均摊复杂度为反阿克曼函数,接近线性。

    Source

    int find(int x)//返回x的父节点,并将其挂载到根节点上
    {
    if(x==fa[x])return x;
    return x=find(fa[x]);
    }
    int link(int x,int y)
    {
    int fx=find(x),fy=find(y);
    if(fx!=fy)fa[fx]=fy;
    }

Example CodeForces277A LearningLanguages

Description

  • The "BerCorp" company has got n employees. These employees can use m approved official languages for the formal correspondence. The languages are numbered with integers from 1 to m. For each employee we have the list of languages, which he knows. This list could be empty, i. e. an employee may know no official languages. But the employees are willing to learn any number of official languages, as long as the company pays their lessons. A study course in one language for one employee costs 1 berdollar.
  • Find the minimum sum of money the company needs to spend so as any employee could correspond to any other one (their correspondence can be indirect, i. e. other employees can help out translating).
  • BerCorp公司有n名雇员。这些雇员共掌握m种官方语言(以从1到m的整数编号)用于正式交流。对于每个雇员,我们有一个他掌握的语言列表,列表可以为空,这意味着一个雇员可能不掌握任何官方语言。但是雇员们愿意学习语言,只要公司为课程付费。每名雇员学习一种语言需要花费 1 Ber元。请找出能让所有雇员直接或间接(可由其他雇员提供中间翻译)交流的最小花费。

Input&Output

Input

  • The first line contains two integers n and m (2?≤?n,?m?≤?100) — the number of employees and the number of languages.
  • Then n lines follow — each employee‘s language list. At the beginning of the i-th line is integer ki (0?≤?ki?≤?m) — the number of languages the i-th employee knows. Next, the i-th line contains ki integers — aij (1?≤?aij?≤?m) — the identifiers of languages the i-th employee knows. It is guaranteed that all the identifiers in one list are distinct. Note that an employee may know zero languages.
  • The numbers in the lines are separated by single spaces.
  • 第一行为两个整数n,m(2<=n,m<=100),为雇员的数量和语言的数量。
  • 接下来n行,每行首先有一个整数ki(0<=ki<=m),为雇员i掌握的语言数量,接下来有ki个整数,为雇员i掌握的语言。这意味着一个表中所有的编号都不同。注意一个雇员可能掌握0种语言。
  • 每行中的数字都用一个空格隔开。

Output

  • Print a single integer — the minimum amount of money to pay so that in the end every employee could write a letter to every other one (other employees can help out translating).
  • 一个整数——能让所有雇员直接或间接交流的最小花费。

Solution

  • 非常裸的并查集,同时需要维护联通块,处理结束后,输出联通块个数-1。容易证明:如果读入结束后仍有大于1个联通块,对于联通块A,B,一定存在至少一种语言,A中有人掌握,且B中无人掌握,此时令B中一人学习一种该语言即可。以此类推,直到联通块总数为1,则需要sum-1个人学习新语言。
    注意一些细节:
  • 可以开一个二维vector,维护掌握每种语言的员工编号,每当读入一名员工掌握一种语言,尝试将其与所有已知的掌握该语言的员工联通,完成后将其压入vector。
  • 如果所有员工都掌握零种语言,答案应为员工数,因为此时所有员工都要学习一门语言。
  • 代码如下:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #define maxn 105
    using namespace std;
    int n,m,k,l,spj,sum,fa[maxn];//sz[maxn];
    vector<vector<int> > v(maxn);
    int find(int x)
    {
    if(x==fa[x])return x;
    else return fa[x]=find(fa[x]);
    }
    void link(int x,int y)
    {
    int fx=find(x),fy=find(y);
    if(fx!=fy) fa[fx]=fy,sum--;
    }
    int main()
    {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        fa[i]=i;
        scanf("%d",&k);
        if(!k){spj++;sum++;continue;}
        sum++;
        for(int j=1;j<=k;++j)
        {
            scanf("%d",&l);
            for(int p=0;p<v[l].size();++p)link(i,v[l][p]);
            v[l].push_back(i);
        }
    }
    if(spj==n){printf("%d",n);return 0;}
    printf("%d",sum-1);
    return 0;
    }

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

❤️数据结构入门❤️(2 - 5)- 并查集

并查集(入门)

并查集题目从入门到入土

并查集入门

并查集——poj1611(入门)

ACM入门之并查集