并查集模板使用时需要注意一下,不同的题目对并查集的要求不同,某些题目的并查集可能需要携带附加信息,这时需要修改模板的并查集初始化和合并内容,而本并查集模板只支持对并查集结点的父节点、子树深度、子树元素个数进行初始化、合并、查找工作!
/* 并查集模板 By:qpswwww(ZYK) 包含findSet(),makeSet(),Union(),使用路径压缩式查找 注:不同的题目对合并的要求不同,并查集也可能携带附加信息 */ #define MAXN 30050 int f[MAXN],depth[MAXN],num[MAXN]; //f[i]=点i的父节点,depth[i]=点i的深度,num[i]=i结点对应下面的元素个数 void makeSet(int i) //并查集初始化 { f[i]=i; num[i]=1; depth[i]=1; } int findSet(int x) //带路径压缩的并查集查找 { if(f[x]==x) return x; return f[x]=findSet(f[x]); } void Union(int a,int b) //将a、b合并,有深度优化 { if(a==b) return; if(depth[a]>depth[b]) {f[b]=a; num[a]+=num[b];} else if(depth[a]<depth[b]) {f[a]=b; num[b]+=num[a];} else {f[a]=b; depth[b]=depth[b]+1; num[b]+=num[a];} }
例题1:POJ 1611 The Suspects
Description
Input
Output
Sample Input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output
4
1
1
Source
题目大意为有多组输入数据,对于每组数据,有n个元素及m组元素,每一组元素对应一个集合,一个元素可存在于多个集合中,与邻元素A在同一集合的所有元素均为A的邻元素,0为最初的邻元素,求每组数据中邻元素的个数
题目可以直接套用上面的目标,下面是代码
/* 并查集模板 By:qpswwww(ZYK) 包含findSet(),makeSet(),Union(),使用路径压缩式查找 注:不同的题目对合并的要求不同,并查集也可能携带附加信息 */ #include <stdio.h> #define MAXN 30050 int f[MAXN],depth[MAXN],num[MAXN]; //f[i]=点i的父节点,depth[i]=点i的深度,num[i]=i结点对应下面的元素个数 void makeSet(int i) //并查集初始化 { f[i]=i; num[i]=1; depth[i]=1; } int findSet(int x) //带路径压缩的并查集查找 { if(f[x]==x) return x; return f[x]=findSet(f[x]); } void Union(int a,int b) //将a、b合并,有深度优化 { if(a==b) return; if(depth[a]>depth[b]) {f[b]=a; num[a]+=num[b];} else if(depth[a]<depth[b]) {f[a]=b; num[b]+=num[a];} else {f[a]=b; depth[b]=depth[b]+1; num[b]+=num[a];} } int main() { int i,j,k,n,m,a,b,roota,rootb; while(1) { for(i=0;i<MAXN;i++) makeSet(i); scanf("%d%d",&n,&m); if(n==0&&m==0) break; for(i=1;i<=m;i++) { scanf("%d%d",&k,&a); for(j=1;j<k;j++) { scanf("%d",&b); { roota=findSet(a); rootb=findSet(b); Union(roota,rootb); //小组里的人都合并在一个集合里 } } } roota=findSet(0); printf("%d\n",num[roota]); } return 0; }
例题2:POJ 2524 Ubiquitous Religions
Description
Input
Output
Sample Input
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
Sample Output
Case 1: 1
Case 2: 7
Hint
Source
题目大意是给出多组数据,对于每组数据,给出n,m,即n个元素,后面接m行,每行有两个数a,b,a和b在同一集合内,求互不相同的集合个数
先将每一对元素合并,所有元素合并完成后,遍历一遍n个元素,找出先根节点的个数,即不同的集合个数
#include <stdio.h> #define MAXN 50050 int f[MAXN],depth[MAXN],num[MAXN]; //f[i]=点i的父节点,depth[i]=点i的深度,num[i]=i结点对应下面的元素个数 void makeSet(int i) //并查集初始化 { f[i]=i; num[i]=1; depth[i]=1; } int findSet(int x) //带路径压缩的并查集查找 { if(f[x]==x) return x; return f[x]=findSet(f[x]); } void Union(int a,int b) //将a、b合并,有深度优化 { if(a==b) return; if(depth[a]>depth[b]) {f[b]=a; num[a]+=num[b];} else if(depth[a]<depth[b]) {f[a]=b; num[b]+=num[a];} else {f[a]=b; depth[b]=depth[b]+1; num[b]+=num[a];} } int main() { int i,j,n,m,a,b,tot=0,sum,roota,rootb; while(++tot) { sum=0; for(i=0;i<MAXN;i++) makeSet(i); scanf("%d%d",&n,&m); if(n==0&&m==0) break; for(i=1;i<=m;i++) { scanf("%d%d",&a,&b); roota=findSet(a); rootb=findSet(b); Union(roota,rootb); } for(i=1;i<=n;i++) if(findSet(i)==i) sum++; //统计不同集合(根节点)个数 printf("Case %d: %d\n",tot,sum); } return 0; }