并查集模板:
适用范围:
对数据进行合并,查询某个数据的祖先,建立数据间的联系。
int fathe[mx] //根节点
int rank[x] //树的高度
void ini()
{
for(int i=0;i<=n;i++){
father[i]=i;
rank[i]=0;
}
}
int find(int x)
{
if(father[x]==x)
return x;
else
father[x]=find(father[x]); //合并根节点,路径压缩
}
int unite(int x,int y)
{
int a=find(x);
int b=find(y);
if(rank[x]<rank[y])
father[x]=y;
else
{
father[y]=x; //向左合并
if(rank[x]==rank[y])
rank[x]++; //增加高度
}
}
例题:
(1):Wireless Network
题意: 有n台电脑,电脑的覆盖范围为k米,在覆盖范围内任意两台电脑可以互联,现在所有的电脑都损坏了,给你这n台电脑的坐标,和两种操作:O表示修复电脑p,S表示检测两台电脑是否可以互联。
思路: 用结构体数组存贮电脑的位置坐标,用数组存储所有以修复电脑的序号,当执行完修复操作时,将当前电脑与所有以修复电脑进行合并操作(合并前判定两台电脑间的距离)。当执行检查操作时 ,判断两台电脑是否为同一组。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct stu
{
double x;
double y;
}f[1010];
int n,book[1010],num,fa[1010],k;
void ini()
{
for(int i=0;i<=1005;i++)
fa[i]=i;
memset(book,0,sizeof(book));
}
int find(int x)
{
if(fa[x]==x)
return x;
else
return fa[x]=find(fa[x]); //更新根节点
}
void unite(int x,int y)
{
int a,b;
a=find(x);
b=find(y);
if(a!=b)
fa[b]=a; //合并根节点
}
int check(int x,int y) //判断距离
{
return (f[x].x-f[y].x)*(f[x].x-f[y].x)+(f[x].y-f[y].y)*(f[x].y-f[y].y);
}
int main()
{
memset(f,0,sizeof(f));
char ch;
int x,y;
cin>>n>>k;
ini();
for(int i=1;i<=n;i++)
scanf("%lf%lf",&f[i].x,&f[i].y);
getchar();
while(scanf("%c",&ch)!=EOF)
{
if(ch=='O')
{
scanf("%d",&x);
for(int i=0;i<num;i++){
if(check(book[i],x)<=k*k)
unite(book[i],x); //合并
// cout<<find(x)<<endl;
}
book[num++]=x; //存储以修复的电脑序号
}
else if(ch=='S')
{
scanf("%d%d",&x,&y);
// cout<<find(3)<<endl;
// cout<<find(2)<<" "<<find(1)<<" "<<find(4)<<endl;
if(find(x)==find(y))
cout<<"SUCCESS"<<endl;
else
cout<<"FAIL"<<endl;
}
getchar();
}
return 0;
}
(2)The Suspects
题意: 给定多个学生组,每组有k个学生,当每一组有一个学生可能患病则该组所有学生都患病,初始只有0号学生可能患病,求可能患病学生数。
思路: 标准的并查集,记录每组的第一个学生,然后其他学生与其合并,最后记录所有学生中与零号学生为同一组的就可以。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> q[510];
int n,m,k,f[30010];
void ini() //并查集模板
{
for(int i=0;i<=n;i++)
f[i]=i;
}
int find(int x)
{
if(f[x]==x)
return x;
else
return f[x]=find(f[x]);
}
void unite(int x,int y)
{
int a=find(x);
int b=find(y);
if(a!=b)
f[b]=a;
}
int main()
{
while(scanf("%d%d",&n,&m)&&n!=0||m!=0)
{
int ans=0;
ini();
for(int i=0;i<m;i++){
int x,t;
scanf("%d%d",&k,&t);
for(int j=0;j<k-1;j++){
scanf("%d",&x);
unite(t,x);
}
}
for(int i=0;i<=n;i++){
if(find(i)==find(0)) //判断是否有相同根节点
ans++;
}
cout<<ans<<endl;
}
return 0;
}