并查集的初步学习
用一句话描述一下并查集:并查集是用树来表示集合,他把每一个连通分量看做一个集合,其中包含了连通分量中的所有点,其中一个连通分量中的点都有共同的父亲结点。
具体的构造:
1. 初始化,每个点的父节点指向自己。
2. 如果两个点有边,那么就在一个连通分量中,所以指向同一个父节点。
3. 最后所有点都会指向各自的父节点,父节点相同的在一个连通分量中。
基本代码就一行:
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
上几道入门的题目:
hdu 1213 求连通分量的个数
hdu 1232 求最少需要几条边可以使所有点都连通。也是求出连通分量的个数-1即是答案
#include<cstdio>
#include<cstring>
const int N=1001;
int fa[N];
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
//int findfa(int x)
//{
// while(x!=fa[x])x=fa[x]; //循环也可以
// return x;
//}
bool vis[N];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)&&n){
for(int i=1;i<=n;i++)fa[i]=i;
int u,v;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
int x=findfa(u),y=findfa(v);
if(x!=y)fa[x]=y;
}
int ans=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
if(!vis[findfa(i)])ans++;
vis[findfa(i)]=1;
}
printf("%d\n",ans-1);
}
return 0;
}
hdu 1272
题意:
给出一些边,要求这些边连成一个连通图,并且无环,就是构成一棵生成树
分析:
用并查集做,如果两点在连边的时候,他们的父节点相同,说明他两已经在一个连通图了,而现在又连上一条边,构成环。
//hdu 1272
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100002;
int fa[N];
bool vis[N];
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
int main()
{
int u,v;
while(~scanf("%d%d",&u,&v)){
if(u==-1&&v==-1)break;
if(u==0&&v==0){ //需要注意这里,输出Yes
printf("Yes\n");continue;
}
memset(vis,0,sizeof(vis));
for(int i=0;i<N;i++)fa[i]=i;
int x=findfa(u),y=findfa(v);
fa[x]=y;
vis[u]=vis[v]=1;
int t=u;
bool flag=1;
while(scanf("%d%d",&u,&v)&&(u||v)){
x=findfa(u),y=findfa(v);
if(x==y)flag=false; //如果两个点的父结点相同,说明有环
fa[x]=y;
vis[u]=vis[v]=1;
}
if(!flag){
printf("No\n");continue;
}
x=findfa(t);
for(int i=1;i<N;i++){
if(vis[i]&&findfa(i)!=x){
flag=false;break;
}
}
if(flag)printf("Yes\n");
else printf("No\n");
}
return 0;
}
题目:hdu 1198
题意:
给出一些图片,图片上水管可以相接的就可以连通,求连通块数。
分析:
可以现根据图片是否可以连通,建一个图,然后再用并查集就好。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=55;
int fa[N*N];
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
int a[][4]={
{1,0,0,1},{1,1,0,0},{0,0,1,1},{0,1,1,0},{1,0,1,0},
{0,1,0,1},{1,1,0,1},{1,0,1,1},{0,1,1,1},{1,1,1,0},{1,1,1,1}};
char g[N][N];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
if(n<0||m<0)break;
for(int i=0;i<n;i++)scanf("%s",g[i]);
for(int i=0;i<n*m;i++)fa[i]=i;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
int x=findfa(i*m+j),y;
if(j+1<m&&a[g[i][j]-'A'][1]&&a[g[i][j+1]-'A'][3]){
y=findfa(i*m+j+1);
if(x!=y)fa[y]=x;
}
if(i+1<n&&a[g[i][j]-'A'][2]&&a[g[i+1][j]-'A'][0]){
y=findfa((i+1)*m+j);
if(x!=y)fa[y]=x;
}
}
}
int ans=0;
for(int i=0;i<n*m;i++)
if(findfa(i)==i)ans++;
printf("%d\n",ans);
}
return 0;
}