【并查集】poj 1703 Find them, Catch them(二集合问题)
题目描述:
http://poj.org/problem?id=1703
中文大意:
城市中存在着两个帮派,警察需要根据获得的信息,判断抓捕的两个帮派分子是否属于同一帮派。
D [a] [b] 语句,表示 a 和 b 属于不同的帮派,
A [a] [b] 语句,是在询问 a 和 b 是否属于同一个帮派。
思路:
我们规定:若 i 属于集合 A ,则 i+n 属于集合 B。
两个帮派可以抽象为两个集合,x 和 y 是两名帮派分子。
因为 x 和 y 属于不同的集合,所以 x 和 y+n 属于同一集合,y 和 x+n 属于同一集合。
判断 x 和 y 是否属于同一集合,只需判断二者所属集的根节点是否相同。
若 root[x] = root[y],则说明二者在一个集合中,
若 root[x] = root[y+n],则说明二者在不同集合中,
若以上两种情况都不满足,则说明二者所属情况还不确定。
代码:
#include<iostream>
using namespace std;
int sets[200002];//各节点的所属集
int height[200002];
//初始化,每个节点属于独立的集合
void init(int n){
for(int i=1;i<=n;i++){
sets[i] = i;
height[i] = 1;
}
}
//查询节点 x 的所属集
int find(int x){
//寻找根节点
int root = x;
while(root != sets[root]){
root = sets[root];
}
//路径压缩:将路径上各节点的所属集修改为根节点
int i = x;
while(sets[i] != root){
int j = sets[i];
sets[i] = root;
i = j;
}
return root;
}
//合并
void union_set(int x, int y){
//寻找节点 x 的所属集
x = find(x);
//寻找节点 y 的所属集
y = find(y);
//若两个节点的所属集树高相同,则将 x 的所属集并到 y 的所属集
if(height[x] == height[y]){
sets[x] = y;
height[y]++;
}//否则,将矮树并到高树,高树的树高保持不变
else if(height[x] > height[y]){
sets[y] = x;
}
else{
sets[x] = y;
}
}
int main(){
int t,n,m;
scanf("%d", &t);
while(t--){
scanf("%d %d", &n, &m);
//集合 1:1 ~ n
//集合 2:n+1 ~ 2*n
init(2*n);
char c;
int x,y;
for(int i=0;i<m;i++){
getchar();
scanf("%c %d %d", &c, &x, &y);
if(c == 'A'){
if(find(x) == find(y)){
printf("In the same gang.\n");
}
else if(find(x) == find(y+n)){
printf("In different gangs.\n");
}
else{
printf("Not sure yet.\n");
}
}
else if(c == 'D'){
//规定:如果 i 属于帮派 A,那么 i+n 属于帮派 B
union_set(x, y+n);
union_set(y, x+n);
}
}
}
}