您好,欢迎来到爱站旅游。
搜索
您的当前位置:首页并查集练习

并查集练习

来源:爱站旅游

A.畅通工程

题意:
n个城市,m条道路,问还需要多少条道路能将n个城市连起来(不必需要直接道路)

Sol:
根据m条道路,将n个城市进行合并操作,最终找出有多少个连通块,答案为联通块的数量减1.(n个城市连起来需要n-1条道路)

Code:

#include<bits/stdc++.h>
using namespace std;

struct UNF{
    vector<int>p;
    void init(int n)
    {
        for(int i = 0; i <= n + 10; ++i) {
            p.push_back(i);
        }
    }
    int find(int x)
    {
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }
    void Union(int a, int b)
    {
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) 
        {
            p[pa] = pb;
        }
    }
};

int main(){
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        if(!n) break;
        UNF city;
        city.init(n);
        while(m -- )
        {
            int x, y;
            scanf("%d%d", &x, &y);
            city.Union(x, y);
        }
        int cnt = 0; 
        for(int i = 1; i <= n; ++i)
            cnt += (city.p[i] == i);
        printf("%d\n", cnt - 1);
    }
    return 0;
}

C.The Suspects

题意:有n个人,m个团队,团队有k个人,问0所在的团队的人数

Sol:并查集维护联通块的大小

Code:

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;

struct UNF{
    vector<int>p;
    vector<int>f;
    void init(int n)
    {
        for(int i = 0; i <= n + 10; ++i) {
            p.push_back(i);
            f.push_back(1);
        }
    }
    int find(int x)
    {
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }
    void Union(int a, int b)
    {
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) 
        {
            p[pb] = pa;
            f[pa] += f[pb];
        }
    }
};

int main(){
    int n, m;
    while(scanf("%d%d", &n, &m))
    {
        if(!n && !m) break;
        UNF team;
        team.init(n);
        while( m -- )
        {
            int k, x, y;
            scanf("%d", &k);
            for(int i = 1; i <= k; ++i)
            {
                if(i == 1) {
                    scanf("%d", &x);
                }
                else {
                    scanf("%d", &y);
                    team.Union(x, y);
                }
            }
        }
        printf("%d\n", team.f[team.find(0)]);
    }
    return 0;
}

B.Find them, Catch them

题意:有n个人,m次询问。
A 1 2 查询a和b是否是敌人或者是无法判断
D 1 2 a和b是敌人

Sol:
:扩展域并查集。假设1-n为第一个集团,n+1-2*n为第二个集团。
换句话说,敌人的敌人就是朋友。所以对于a和b是敌人进行的并查集合并为,将a和b+n合并以及a+n和b合并
:带权并查集,用d[i]表示与根节点的关系,0表示朋友,1表示敌人
Code:

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;

struct UNF{
    vector<int>p;
    void init(int n)
    {
        for(int i = 0; i <= n + 10; ++i) {
            p.push_back(i);
        }
    }
    int find(int x)
    {
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }
    void Union(int a, int b)
    {
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) 
        {
            p[pa] = pb;
        }
    }
};


int main(){
    int T; scanf("%d", &T);
    while ( T -- ){
        int n, m;
        scanf("%d%d", &n, &m);
        UNF gangs;
        gangs.init(2 * n);
        while( m -- )
        {
            char op[2];
            int a, b;
            scanf("%s%d%d", op, &a, &b);
            if(*op == 'D') {
                gangs.Union(a, b + n);
                gangs.Union(a + n, b);
            }
            else {
                int pa = gangs.find(a);
                int pb = gangs.find(b);
                if(pa == pb) {
                    puts("In the same gang.");
                }
                else if(pa == gangs.find(b + n)) {
                    puts("In different gangs.");
                }
                else {
                    puts("Not sure yet.");
                }
            }
        }
    }
    return 0;
}

D.食物链

题意:有一个食物链,A->B, B->C, C->A。现在有n个动物,编号为1-n,每个动物都是ABC的一种,但是不知道是哪一种,
现在有人用两种说法对这n个动物构成的食物链进行描述。
- 第一种说法:1 X Y 表示X和Y是同一类
- 第二种说法:2 X Y 表示X吃Y
这个人说了K句话,问有多少句话是假话。假话满足一下条件之一,否则就是真话
- 1) 当前的话与前面的某些真的话冲突,就是假话;
- 2) 当前的话中X或Y比N大,就是假话;
- 3) 当前的话表示X吃X,就是假话。

Sol:带权并查集。d[i]表示与根节点的关系,0表示同类,1表示i吃根节点,2表示根节点吃i

Code:

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
// 食物链
// 带权并查集
struct UNF{
    vector<int>p;
    vector<int>d;
    void init(int n)
    {
        for(int i = 0; i <= n + 10; ++i) {
            p.push_back(i);
            d.push_back(0);
        }
    }
    int find(int x)
    {
        if(x != p[x]) {
            int t = p[x];
            p[x] = find(p[x]);
            d[x] = (d[x] + d[t]) % 3;
        }
        return p[x];
    }
    void Union(int a, int b)
    {
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) 
        {
            p[pa] = pb;
        }
    }
};

int main(){
    int n,  k;
    scanf("%d%d", &n, &k);
    UNF ch;
    ch.init(n);
    int d, x, y;
    int cnt = 0;
    while( k -- )
    {
        scanf("%d%d%d", &d, &x, &y);
        if(x > n || y > n || (x == y && d == 2)) ++ cnt;
        else {
            int px = ch.find(x);
            int py = ch.find(y);
            if(px != py) {
                ch.p[px] = py;
                ch.d[px] = (ch.d[y] - ch.d[x] + d - 1) % 3;
            }
            else {
                -- d;
                if(d != (ch.d[x] - ch.d[y] + 3) %  3) cnt ++;
            }
        }
    }
    cout << cnt << endl;
    return 0;
}

E.Rochambeau

题意:有n个人在玩石头剪刀布,然后有m个回合。其中一个是裁判,其余人分成a,b,c三组,同组的人只会出一个手势。
问你是否能找到这个裁判,如果能找到输出最早能在在第几回合能找到
如果有多个,输出"Can not determine"
如果不存在,输出"Impossible"

Sol:首先分组可以由并查集维护,同上题食物链一样,d[i]表示与根节点的关系,0表示同组,1表示输给根节点,2表示赢根节点
然后去枚举n个人作为裁判时的情况。如果有cnt个人符合
- cnt > 1 无法确定
- cnt == 0 不存在
- cnt == 1 存在唯一裁判,那么就是求最早在第几回合能判断出。我们只需找出其他人矛盾的最大回合。

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;

struct UNF{
    vector<int>p;
    vector<int>d;
    void init(int n)
    {
        for(int i = 0; i <= n + 10; ++i) {
            p.push_back(i);
            d.push_back(0);
        }
    }
    int find(int x)
    {
        if(x != p[x]) {
            int t = p[x];
            p[x] = find(p[x]);
            d[x] = (d[x] + d[t]) % 3;
        }
        return p[x];
    }
};

struct node{
    int a, b;
    int c;
}A[2020];

int idx;
int n, m;

bool judge(int u)
{
    UNF ch;
    ch.init(n);
    for(int i = 1; i <= m; ++i)
    {
        int x = A[i].a;
        int y = A[i].b;
        int d = A[i].c;
        if(x == u || y == u) continue;
        int fx = ch.find(x);
        int fy = ch.find(y);
        if(fx == fy)
        {
            if(d != (ch.d[x] - ch.d[y] + 3) % 3) {
                idx = max(idx, i);
                return false;
            }
        }
        else {
            int px = ch.find(x);
            int py = ch.find(y);
            ch.p[px] = py;
            ch.d[px] = (ch.d[y] - ch.d[x] + d + 3) % 3;
        }
    }
    return true;
}

int main(){
    while(scanf("%d%d", &n, &m) != EOF)
    {
        for(int i = 1; i <= m; ++i)
        {
            int a, b;
            char ch;
            scanf("%d%c%d", &a, &ch, &b);
            if(ch == '=') A[i] = {a, b, 0};
            else if(ch == '<') A[i] = {a, b, 1};
            else A[i] = {a, b, 2};
        }
        idx = 0;
        int ans = 0;
        int cnt = 0;
        for(int i = 0; i < n; ++i)
        {
            if( judge(i) ) {
                ++ cnt;
                ans = i;
            }
        }  
        if(cnt==1) printf("Player %d can be determined to be the judge after %d lines\n", ans, idx);
        else if(cnt > 1) printf("Can not determine\n");
        else printf("Impossible\n");
    }
    return 0;
}

update 2021/12/19

Other 用法

题意:
q q q次次操作

  • 1 x 向数组后面插入 x x x
  • 2.x y 将数组中所有 x x x该为 y y y

sol
很容易想到用并查集来维护这些值变成了什么,但是当我们正着去做的时候,后面如果要改变前面已经该过了的值话就会出问题。比如前面你要把1变成2,后面又让1变成3,那么这样操作下来的话,最后1都会变成3 。那要怎么避免呢,我们将操作保存下来;然后反着去维护即可。具体细节看代码

Code


#include <iostream>
using namespace std;
const int N = 5e5 + 10;
int p[N];
int opt[N], x[N], y[N];

void solve()
{
    int q;
    cin >> q;
    rep(i, 1, 5e5) p[i] = i;
    for (int i = 1; i <= q; ++ i) {
        cin >> opt[i] >> x[i];
        if(opt[i] == 2) {
            cin >> y[i];
        }
    } 
    vector<int> res;
    for(int i = q; i ; i -- ) {
        if(opt[i] == 1) res.push_back(p[x[i]]);
        else {
            p[x[i]] = p[y[i]];
        }
    }
    reverse(ALL(res));
    for(int x : res) cout << x << " ";
}

signed main(){

    
    int T = 1;
    // cin >> T;
    while(T --) solve();
    
    return 0;
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- azee.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务