zoukankan      html  css  js  c++  java
  • 关于并查集的一些问题以及模版

      对于并查集,有个有趣的小故事可以加深对并查集的理解

      小故事

      话说江湖上散落着各式各样的大侠,有上千个之多。他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。但大侠们有一个优点就是讲义气,绝对不打自己的朋友。而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。这样一来,江湖上就形成了一个一个的群落,通过两两之间的朋友关系串联起来。而不在同一个群落的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢? 我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物,这样,每个圈子就可以这样命名“齐达内朋友之队”“罗纳尔多朋友之队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。 但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长,要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?” 这样一来,队长面子上挂不住了,而且效率太低,还有可能陷入无限循环中。于是队长下令,重新组队。队内所有人实行分等级制度,形成树状结构,我队长就是根节点,下面分别是二级队员、三级队员。每个人只要记住自己的上级是谁就行了。遇到判断敌友的时候,只要一层层向上问,直到最高层,就可以在短时间内确定队长是谁了。由于我们关心的只是两个人之间是否连通,至于他们是如何连通的,以及每个圈子内部的结构是怎样的,甚至队长是谁,并不重要。所以我们可以放任队长随意重新组队,只要不搞错敌友关系就好了。于是,门派产生了。

      看过了这个故事,是不是对并查集有了更深的认识。并查集其实本质上就是画圈,将一些有联系的圈起来,而在查询方面也更加迅速。

      先把模版贴一下

      模版

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int par[10005];
    int Rank[10005];
    void init(int n){
        for(int i=0;i<=n;i++){
            par[i]=i;
            Rank[i]=0;
        }
    }
    int find(int x){
        if(par[x]==x) return x;
        return par[x]=find(par[x]);
    }
    void join(int x,int y){
        x=find(x);
        y=find(y);
        if(x==y) return ;
        if(Rank[x]<Rank[y]) par[x]=y;
        else {
            par[y]=x;
            if(Rank[x]==Rank[y]) Rank[x]++;
        }
    }
    bool same(int x,int y){
        return find(x)==find(y);
    }

      然后在做pta上做题的时候做了两道入门的并查集的题目

    L2-024 部落

    在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。

    输入格式:

    输入在第一行给出一个正整数N(≤),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:

    P[1P[2⋯ P[K]

    其中K是小圈子里的人数,P[i](i=1,,K)是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过1。

    之后一行给出一个非负整数Q(10^4​​),是查询次数。随后Q行,每行给出一对被查询的人的编号。

    输出格式:

    首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y,否则输出N

    输入样例:

    4
    3 10 1 2
    2 3 4
    4 1 5 7 8
    3 9 6 4
    2
    10 5
    3 7
    

    输出样例:

    10 2

    Y

    N

    思路

    其实就是并查集的应用而已,只不过加了一个区分互不相交,这个就直接算有多少个根就行了

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int par[10005];
    int Rank[10005];
    void init(int n){
        for(int i=0;i<=n;i++){
            par[i]=i;
            Rank[i]=0;
        }
    }
    int find(int x){
        if(par[x]==x) return x;
        return par[x]=find(par[x]);
    }
    void join(int x,int y){
        x=find(x);
        y=find(y);
        if(x==y) return ;
        if(Rank[x]<Rank[y]) par[x]=y;
        else {
            par[y]=x;
            if(Rank[x]==Rank[y]) Rank[x]++;
        }
    }
    bool same(int x,int y){
        return find(x)==find(y);
    }
    int main()
    {
        int n,m;
        set<int>num;
        cin>>n;
        init(10000);
        for(int i=0;i<n;i++){
            int k,a,b;
            cin>>k>>a;
            num.insert(a);
            for(int i=1;i<k;i++){
                cin>>b;
                num.insert(b);
                join(a,b);
                a=b;
            }
        }
        cout<<num.size()<<' ';
        set<int> t;
        for(int i=1;i<=10000;i++){
            if(num.count(i)) t.insert(find(i));
        }
        cout<<t.size()<<endl;
        cin>>m;
        for(int i=0;i<m;i++){
            int a,b;
            cin>>a>>b;
            if(same(a,b)) cout<<'Y'<<endl;
            else cout<<'N'<<endl;
        }
        return 0;
    }

    L2-010 排座位

    布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。

    输入格式:

    输入第一行给出3个正整数:N≤100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:宾客1 宾客2 关系,其中关系为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K行,每行给出一对需要查询的宾客编号。

    这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。

    输出格式:

    对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出No problem;如果他们之间并不是朋友,但也不敌对,则输出OK;如果他们之间有敌对,然而也有共同的朋友,则输出OK but...;如果他们之间只有敌对关系,则输出No way

    思路

    纯粹的并查集,再加个二维数组,表示两两敌对关系。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int par[105];
    int Rank[105];
    void init(int n){
        for(int i=0;i<=n;i++){
            par[i]=i;
            Rank[i]=0;
        }
    }
    int find(int x){
        if(par[x]==x) return x;
        return par[x]=find(par[x]);
    }
    void join(int x,int y){
        x=find(x);
        y=find(y);
        if(x==y) return ;
        if(Rank[x]<Rank[y]) par[x]=y;
        else {
            par[y]=x;
            if(Rank[x]==Rank[y]) Rank[x]++;
        }
    }
    bool same(int x,int y){
        return find(x)==find(y);
    }
    int main()
    {
        int n,m,k;
        int num[105][105];
        memset(num,0,sizeof(num));
        cin>>n>>m>>k;
        init(n);
        for(int i=0;i<m;i++){
            int a,b,c;
            cin>>a>>b>>c;
            if(c==-1) num[a][b]=num[b][a]=c;
            else join(a,b);
        }
        for(int i=0;i<k;i++){
            int a,b;
            cin>>a>>b;
            if(num[a][b]==-1){
                if(same(a,b)) cout<<"OK but..."<<endl;
                else cout<<"No way"<<endl;
            }
            else {
                if(same(a,b)) cout<<"No problem"<<endl;
                else cout<<"OK"<<endl;
            }
        }
        return 0;
    }
  • 相关阅读:
    成都的收藏品市场
    微信小程序 如何定义全局函数?
    Linux下 安装VMware Tools工具
    小程序圆角进度条实现方法
    Excel 将换行符替换为空
    再次学习mysql优化
    Subl 命令
    时间见证着—eternal life
    大巧不工web前端设计修炼之道—笔记
    批量更新某字段内容
  • 原文地址:https://www.cnblogs.com/maybe96/p/10449572.html
Copyright © 2011-2022 走看看