ACM_基础并查集

基础并查集: 用于高效的查找某两个元素是否属于同一个集合;

一.普通的查找方式:

  1. 初始化所有的元素的父亲都是它自己:
    for(int i = 1; i <= n; i++) pa[i] = i;

  2. find函数, 每次向上找它的父亲, 直到找到它的根(根的特点是pa[i] == i):
    int find(int x){
    return x == pa[x] ? x : find(pa[x]);
    }
    这里写图片描述

  3. union_set函数:连接2个集合,实现的方法是用find函数找到2个集合的根分别为fx, fy, pa[fx] = fy; 就连接好了2个集合:
    Void union_set(int x, int y){
    int fx = find(x);
    int fy = find(y);
    pa[fx] = fy;
    }
    这里写图片描述

然后每次只需要判断一下是否find(x) == find(y)如果是说明他们是同一个集合, 否则不同集合;

二.并查集的实现方式:

  • 和上面的方法大致相同, 唯一不同的是find函数; 每次find之后都把该集合的该支链的所有元素的父亲改成根节点;这样下次再find的时候只需要找一次就知道该元素属于哪个集合了;实现的方式也很相同,只需要把find函数稍微改一下:
    int find(int x){
    return x == pa[x] ? x : pa[x] = find(pa[x]);
    }
    这里写图片描述
    当然这里树的深度还比较小了还不感觉什么, 但假如树足够深100层, 1000层的时候呢? 并查集会省下大量的时间;

例子

题目链接:http://poj.org/problem?id=1611
题目大意: 中文就不解释了;
做法:把同一个集合的所有元素都放到同一个集合里, 当放完之后, 检查一下0号同学在哪个集合, 再判断一下剩下的同学是否和它在同一个集合里面;

//
//  Created by fkjs on 2015-09-17
//  Copyright (c) 2015 fkjs. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <stack>
#include <vector>
#define clr(x) memset(x, 0, sizeof(x))

using namespace std;
const int INF = 0x3f3f3f3f;
const int maxm = 505;
const int maxn = 30000 + 10;
typedef long long int ll;

int n, m;
int pa[maxn];

int find(int x){//并查集的基础->find函数, 它的特点就是pa[x] = find(pa[x]), 这一语句可以把该路径上的所有点的父亲都改成根节点;
    return x == pa[x] ? x : pa[x] = find(pa[x]);
}

void connect(int x, int y){//链接两个并查集
    int fa = find(x);//找到根节点, 当然找的过程中会更新路上的点;
    int fb = find(y);//同上;
    pa[fa] = fb;//链接两个集合;
}

int main(void) {
#ifdef LOCAL
    //freopen("C:\\Users\\Administrator\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\Administrator\\Desktop\\out.txt", "w", stdout);
#endif
    //ios_base::sync_with_stdio(0);

    while(scanf("%d%d", &n, &m) == 2 && (n || m)){
        for(int i = 0; i < n; i++) pa[i] = i;
        for(int i = 0; i < m; i++){
            int len; scanf("%d", &len);
            int x; scanf("%d", &x);
            int tp = x;
            for(int i = 1; i < len; i++){
                scanf("%d", &x);
                connect(x, tp);
                tp = x;
            }
        }
        int ans = 0;
        int p = find(0);//找到最初感染者所在的集合, 它的根是p;
        for(int i = 0; i < n; i++)//凡是根是p的人都被感染了;
            if(find(i) == p)
                ans++;
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值