基础并查集: 用于高效的查找某两个元素是否属于同一个集合;
一.普通的查找方式:
初始化所有的元素的父亲都是它自己:
for(int i = 1; i <= n; i++) pa[i] = i;find函数, 每次向上找它的父亲, 直到找到它的根(根的特点是pa[i] == i):
int find(int x){
return x == pa[x] ? x : find(pa[x]);
}
- 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;
}