并查集和带权并查集模板及一些习题

并查集是一种非常优美的数据结构,并查集是用来解决一些不交集的合并以及查询问题。
并查集模板

int get(int a)
{
    if(a==f[a])
        return a;
    return f[a] = get(f[a]);
}
void merge(int a,int b)
{
    a = get(a);
    b = get(b);
    if(a!=b)
    {
        f[a] = b;
    }
}

How Many Tables
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213
题意:认识的人可以做一个桌子,可以直接认识也可以间接认识,问你最少需要几张桌子。
题解:比较裸的一个并查集题,直接使用模板即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
int f[maxn];
int n, m;
void init()
{
    for (int i = 1; i <= n;i++)
    {
        f[i] = i;
    }
}
int get(int a)
{
    if(a==f[a])
        return a;
    return f[a]=get(f[a]);
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n >> m;
        init();
        for (int i = 1; i <= m;i++)
        {
            int a, b;
            cin >> a >> b;
            a = get(a);
            b = get(b);
            if(a!=b)
                f[a] = b;
        }
        int ans = 0;
        for (int i = 1; i <= n;i++)
        {
            if(f[i]==i)
                ans++;
        }
        cout << ans << endl;
    }
    return 0;
}

程序自动分析
链接:https://ac.nowcoder.com/acm/problem/17881
题解:这个题也算是一个比较裸的并查集,不过唯一需要的是注意数据范围,这个数据数组肯定开不下,这里我们就需要map来帮助我们了,然后用上板子可以很轻松的写题。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
map<ll, int> m;
ll x[maxn];
ll y[maxn];
ll c[maxn];
int f[maxn * 3];
int get(int a)
{
    if(a==f[a])
        return a;
    return f[a] = get(f[a]);
}
void merge(int a,int b)
{
    a = get(a);
    b = get(b);
    if(a!=b)
    {
        f[a] = b;
    }
}
void init()
{
    for (int i = 0; i < maxn * 3;i++)
    {
        f[i] = i;
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        init();
        int n;
        scanf("%d", &n);
        int cnt = 1;
        m.clear();
        for (int i = 1; i <= n;i++)
        {
            scanf("%lld %lld %d", &x[i], &y[i], &c[i]);
            if(!m[x[i]])
            {
                m[x[i]] = cnt++;
            }
            if(!m[y[i]])
            {
                m[y[i]] = cnt++;
            }
            if(c[i]==1)
            {
                merge(m[x[i]], m[y[i]]);
            }
        }
        int f1 = 1;
        for (int i = 1; i <= n;i++)
        {
            if(c[i]==0)
            {
                if(get(m[x[i]])==get(m[y[i]]))
                {
                    f1 = 0;
                }
            }
        }
        if(f1)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

带权并查集
带权并查集就是在并查集的基础上新定义了一种权值,我们在维护并查集的同时也要维护这个权值,它可以帮助我们方便解决很多问题。
模板

int get(int a)
{
    if(a==f[a])
        return a;
    int root = get(f[a]);
    d[a] += d[f[a]];
    return f[a] = root;
}
void merge(int a,int b)
{
    a = get(a);
    b = get(b);
    if(a!=b)
    {
        f[a] = b;
        d[a] = size[b];
        size[b] += size[a];
    }
}

Cube Stacking
题目链接:http://poj.org/problem?id=1988
题解:这个算是带权并查集的板子题,唯一需要注意的点是我们在查询是还需要进行get操作一次,因为这个点以下的哪些点可能没有更新,如果不get一次,很有可能会wa掉。

#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 3e4 + 5;
int f[maxn];
int s[maxn];
int d[maxn];
int n;
int get(int a)
{
    if(a==f[a])
        return a;
    int root = get(f[a]);
    d[a] += d[f[a]];
    return f[a] = root;
}
void init()
{
    for (int i = 0; i < maxn; i++)
    {
        f[i] = i;
        s[i] = 1;
    }
}
int main()
{
    cin >> n;
    init();
    for (int i = 1; i <= n;i++)
    {
        char z;
        cin >> z;
        if(z=='M')
        {
            int a, b;
            cin >> a >> b;
            a = get(a);
            b = get(b);
            if(a!=b)
            {
                f[a] = b;
                d[a] += s[b]; 
                s[b] += s[a];
                //s[a] = 0;
            }
        }
        else 
        {
            int a;
            get(a);
            cout << d[a] << endl;
        }
    }
    return 0;
}

银河英雄传说
链接:https://ac.nowcoder.com/acm/problem/16889
题解:这个题和上个题唯一的不同就是我们需要判断下这两个点是不是在一个队列上,在判断时我们已经进行过get操作了,所以就不需要再进行get操作了,然后输出间距即可。

#include<algorithm>
#include<iostream>
#include<queue>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
#include<cmath>
#define ll long long
#define pr pair<ll,int>
using namespace std;
const int maxn = 3e5 + 5;
int f[maxn];
int d[maxn];
int size[maxn];
int get(int a)
{
    if(a==f[a])
        return a;
    int root = get(f[a]);
    d[a] += d[f[a]];
    return f[a] = root;
}
void merge(int a,int b)
{
    a = get(a);
    b = get(b);
    if(a!=b)
    {
        f[a] = b;
        d[a] = size[b];
        size[b] += size[a];
    }
}
void init()
{
    for (int i = 1; i <= maxn;i++)
    {
        f[i] = i;
        size[i] = 1;
    }
}
int main()
{
    int n;
    cin >> n;
    init();
    for (int i = 1; i <= n;i++)
    {
        char s;
        cin >> s;
        if(s=='M')
        {
            int a, b;
            cin >> a >> b;
            merge(b, a);
        }
        else
        {
            int a, b;
            cin >> a >> b;
            if(get(a)==get(b))
            {
                cout << abs(d[a] - d[b]) - 1 << endl;
            }
            else
                cout << -1 << endl;
        }
    }
        return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值