并查集的初步学习

用一句话描述一下并查集:并查集是用树来表示集合,他把每一个连通分量看做一个集合,其中包含了连通分量中的所有点,其中一个连通分量中的点都有共同的父亲结点。
具体的构造:
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
",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
");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
");continue;
        }
        x=findfa(t);
        for(int i=1;i<N;i++){
            if(vis[i]&&findfa(i)!=x){
                flag=false;break;
            }
        }
        if(flag)printf("Yes
");
        else printf("No
");
    }
    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
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/01world/p/5651251.html