hdu - 1213 + poj - 2236 + poj - 1611 + hdu -1272 + poj - 1308基础并查集模板题 +路径优化、秩优化方法

本文详细介绍了并查集算法的基本概念、实现原理及其在不同场景中的应用案例,包括生日派对桌位分配、电脑通信距离判定、学校非典传播范围评估等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:一个人过生日邀请了n个朋友来,不过他的朋友不习惯和陌生人在一张桌子上,问你最少要准备几张桌子。若a认识b,b认识c,a也可以和c坐一起。

基础并查集,模板   

路径压缩优化、秩优化

链接:hdu 1213

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;

const int maxn = 1050 + 10;
const int inf = 1e9 + 7;
const int N = 1005;// 初始化并查集

int n;
int father[N];

void init() {
    for(int i = 0; i < N; i++)
      father[i] = i;
}
int getfather(int x) {
    while(x != father[x])
      x = father[x];
    return x;
}
// 合并两个元素所在的集合
void unions(int x, int y) {
    x = getfather(x);
    y = getfather(y);
    if(x != y)
       father[x] = y;
}
// 判断两个元素是否属于同一个集合
bool same(int x, int y) {
    return getfather(x) == getfather(y);
}

int x[maxn], y[maxn];
int r[maxn];
int d, cnt = 0;

bool dic(int a, int b) {
    if((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]) <= d * d)
        return 1;
    return 0;
}

int main()
{
    int T, kcase = 0;
    cin >> T;
    while(T--) {
        int m;
        cin >> n >> m;
        init();
        while(m--) {
            int a, b;
            cin >> a >> b;
            unions(a, b);
        }
        m++;
        for(int i = 1; i <= n; i++) {
            if(father[i] == i) {
                m++;
            }
        }
        cout << m << endl;
    }
    return 0;
}
路径压缩

int getfather(int x) {
    if(x != father[x])
      father[x] = getfather(father[x]); // 路径压缩修改的是father数组
    return father[x];
}

秩优化

void unite (int x, int y) {
    x = getfather(x);
    y = getfather(y);
    if(x == y) return;
    if(rnk[x] < rnk[y]) {
        father[x] = y;
    }
    else {
        father[y] = x;
        if(rnk[x] == rnk[y]) rnk[x]++;
    }
}


int getfather(int x) {
    if(x != father[x])
      father[x] = getfather(father[x]); // 路径压缩修改的是father数组
    return father[x];
}


题意:给你N台电脑,从1-N。一个数字,表示两台计算机的最大通信距离,超过这个距离就无法进行通信。然后分别告诉这些电脑的坐标,接下来有两种操作,第一种O表示这点电脑修好,第二种S,表示测试这两台电脑能不能进行正常的通信

修好的遍历看能否通信

链接:poj 2236

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;

const int maxn = 1050 + 10;
const int inf = 1e9 + 7;
const int N = 1005;// 初始化并查集

int n;
int father[N];

void init() {
    for(int i = 0; i < N; i++)
      father[i] = i;
}
// 获取根结点  1
int getfather(int x) {
    while(x != father[x])
      x = father[x];
    return x;
}
/*/ 获取根结点  2
int getfather(int x) {
    if(x != father[x])
      father[x] = getfather(father[x]); // 路径压缩修改的是father数组
    return father[x];
}*/
// 合并两个元素所在的集合
void unions(int x, int y) {
    x = getfather(x);
    y = getfather(y);
    if(x != y)
       father[x] = y;
}
// 判断两个元素是否属于同一个集合
bool same(int x, int y) {
    return getfather(x) == getfather(y);
}

int x[maxn], y[maxn];
int r[maxn];
int d, cnt = 0;

bool dic(int a, int b) {
    if((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]) <= d * d)
        return 1;
    return 0;
}

int main()
{
    scanf("%d %d", &n, &d);
    for(int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i];
    }
    init();
    char s[5];
    int a, b;
    while(scanf("%s", s) != EOF) {
        if(s[0] == 'O') {
            scanf("%d", &a);
            r[++cnt] = a;
            for(int i = 1; i < cnt; i++) {
                if(dic(r[i], a)) {
                    unions(r[i], a);
                }
            }
        }
        else {
            scanf("%d %d", &a, &b);
            if(same(a, b)) {
                puts("SUCCESS");
            }
            else {
                puts("FAIL");
            }
        }
    }
    return 0;
}

题意:有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数。

对于每一组的孩子都和第一个孩子合并就好了

链接:poj 1611

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;

const int maxn = 30050 + 10;
const int inf = 1e9 + 7;

int n;
int father[maxn];
int a[maxn];

void init(int n) {
    for(int i = 0; i <= n; i++)
      father[i] = i;
}
int getf(int x) {
    if(x != father[x])
      father[x] = getf(father[x]); // 路径压缩修改的是father数组
    return father[x];
}
// 合并两个元素所在的集合
void unions(int x, int y) {
    x = getf(x);
    y = getf(y);
    if(x != y)
       father[x] = y;
}

int main()
{
    int m;
    while(scanf("%d %d", &n, &m) && (n || m)) {
        init(n);
        int k;
        while(m--) {
            scanf("%d", &k);
            for(int i = 0; i < k; i++) {
                scanf("%d", &a[i]);
                unions(a[i], a[0]);
            }
        }
        int sum = 0;
        for(int i = 0; i < n; i++) {
            if(getf(i) == father[0]) {
                sum++;
            }
        }
        printf("%d\n", sum);
    }
    return 0;
}

题意:中文题

已经在同一集合的就不是了

链接:hdu 1272

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include <cmath>
using namespace std;

const int maxn = 200000 + 10;
const int inf = 1e9 + 7;

int n;

int father[maxn];
int r[maxn];
int a[maxn];
int cnt, flag;

void init() {
    for(int i = 0; i <= maxn; i++) {
      father[i] = i;
      r[i] = 0;
      a[i] = 0;
      cnt = 0;
      flag = 1;
    }
}
// 获取根结点
int getf(int x) {
    return x == father[x] ? x : father[x] = getf(father[x]);
}
// 合并两个元素所在的集合
bool unions(int x, int y) {
    x = getf(x);
    y = getf(y);
    if(x == y) {
        return false;
    }
    father[x] = y;
    return true;
}

int main()
{
    int T, kcase = 0;
    int m;
    init();
    while(~scanf("%d %d", &n, &m)) {
        if(n == -1 && m == -1) {
            break;
        }
        else if(n == 0 && m == 0) {
            int res = 0;
            for(int i = 0; i < cnt; i++) {
                if(father[a[i]] == a[i]) {
                    res++;
                }
            }
            if(res >= 2) flag = 0;
            if(flag) puts("Yes");
            else puts("No");
            init();
        }
        else {
            if(r[n] == 0) {
                r[n] = 1;
                a[cnt++] = n;
            }
            if(r[m] == 0) {
                r[m] = 1;
                a[cnt++] = m;
            }
            if(!unions(n, m)) {
                flag = 0;
            }
        }
    }
    return 0;
}

题意:和hdu1272差不多,只不过给出的是有向图,问图中的点是否是一颗树。

多记录一下入度判断一下

链接:poj 1308

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <vector>
#include <sstream>
#define ll long long

using namespace std;

const int maxn = 200000 + 10;
const int maxm = 1000000 + 10;
const int inf = 0x3f3f3f3f;

int fa[maxn];
int r[maxn];
int in[maxn];
int num[maxn];

int n, m, k;

void init() {
    for(int i = 0; i < maxn; i++) {
        fa[i] = i;
        in[i] = num[i] = 0;
    }
}

int getf(int x) {
    if(fa[x] != x) {
        fa[x] = getf(fa[x]);
    }
    return fa[x];
}

bool unions(int x, int y) {
    in[y]++;
    x = getf(x);
    y = getf(y);
    if(x == y) {
        return false;
    }
    if(x != y) {
        fa[x] = y;
        return true;
    }
}

int main()
{
    int kcase = 0;
    int T;
    int a, b;
    init();
    int flag = 1;
    while(~scanf("%d %d", &a, &b)) {
        if(a == -1 && b == -1) {
            break;
        }
        else if(a == 0 || b == 0) {
                int ans = 0;
            for(int i = 1; i < maxn; i++) {
                if(num[i] && fa[i] == i) {
                    ans++;
                }
                if(num[i] && in[i] > 1){
                    flag = 0;
                }
            }
            if(ans >= 2) {
                flag = 0;
            }
            if(flag) {
                printf("Case %d is a tree.\n", ++kcase);
            }
            else {
                printf("Case %d is not a tree.\n", ++kcase);
            }
            init();
            flag = 1;
        }
        else {
            num[a] = num[b] = 1;
            if(!unions(a, b)) {
                flag = 0;
            }
        }
    }
    return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值