并查集学习入门到熟悉

我的:
并查集就是把具有相同属性的一些事物归为一类,然后归类之后后就相当于形成了多个联通块,其中要注意的是pre数组记录的是他的上级,然而不一定是他的最上级,判断他是不是最上级的话就是他的pre值是不是等于该数本身,即pre[i] == i,其实有多少个最上级就相当于有多少个连通块。然而想要找到某个数的最上级是哪个数的话就是调用finds函数。finds函数里面有一个状态压缩算法,就是找到他的最上级之后,就把他和他的上几级的pre标为他们的最上级,对之后的操作很有帮助。

这里写图片描述

这个可以参考:http://blog.csdn.net/dellaserss/article/details/7724401,感觉讲的挺好的。

接下来把并查集的一些写的题贴出来吧,并查集不难的。

UVA10608 Friends

这个就是相当于求连通块了,
我的:题意是要找到最多有多少人在同一个集合里面

#include<bits/stdc++.h>
using namespace std;
const int maxn=300000+10;
int pre[maxn];
int num[maxn];
int n,m;

void Init()//初始化
{
    for(int i= 1 ;i <= n;i++)
    {
        pre[i]=i;
        num[i]=1;
    }
}
int finds(int x)
{
    int r=x;
    while(r != pre[r])
    {
        r=pre[r];
    }
    int i=x,j;
    while(i != r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}

void join(int x,int y)
{
    int fx=finds(x),fy=finds(y);
    if(fx != fy){
        pre[fx]=fy;
        num[fy]+=num[fx];
    }
}


int main()
{
    int Tcase;
    scanf("%d",&Tcase);
    for(int ii =1;ii <= Tcase ;ii ++)
    {
        scanf("%d%d",&n,&m);
        Init();
        for(int i=1; i<= m; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            join(x,y);
        }
        int ans=0;
        for(int i=1;i <= n;i++){
        if(num[i]>ans)
            ans= num[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

L2-010. 排座位

这个题就是就是一个连通块,因为朋友的朋友是朋友,而其他的如果没有给出的话就没有必然的联系,就是看他们是不是在一个朋友圈里面了,
之后的判断的敌人的话,就看有没有出现过就可以了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;

const int maxn=100000+10;

int n,m;
int pre[maxn];
void Init()
{
    for(int i=1  ;i <= n;i++)
    {
        pre[i]=i;
    }
}

int finds(int x)
{
    int r=x;
    while(r != pre[r])
    {
        r=pre[r];
    }
    int i=x,j;
    while(i != r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}
void join(int x,int y)
{
    int fx=finds(x),fy=finds(y);
    if(fx != fy)
    {
        pre[fx]=fy;
    }
}

int main()
{
    int s;
    scanf("%d%d%d",&n,&m,&s);
    Init();
    int a[maxn][2];
    int k=0;
    for(int ii=1;ii <= m;ii++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);

        if(z==1)//z==1代表的是朋友,就进行合并
            join(x,y);
        else//代表是敌人,记录下来,之后再进行判断
        {
            a[k][0]=x;
            a[k++][1]=y;
        }
    }
    for(int ii=1;ii <= s;ii++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int flag1=0,flag2=0;
        if(finds(x)==finds(y))
            flag1=1;
        for(int i=0;i<k;i++)
        {
            if((a[i][0]==x&&a[i][1]==y) || (a[i][0]==y&&a[i][1]==x))
            {flag2=1;break;}
        }
        if(flag1 && ! flag2)
            cout<<"No problem"<<endl;
        else if(flag1 && flag2)
            cout<<"OK but..."<<endl;
        else if(!flag1 && flag2)
            cout<<"No way"<<endl;
        else if(!flag1 && !flag2)
        {
            cout<<"OK"<<endl;
        }
    }
    return 0;
}

PAT L2-007. 家庭房产
这个题的预处理有点麻烦,需要把变量的一些值(housenum,area)记录下来,还是有点难度的,表示调了很久,不过到了并查集这块就很简单了。
我的:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;

const int maxn=1000+10;
struct Node
{
    int data;
    int pre;
    int num;
    double housenum;
    double area;
}nod[maxn*maxn/10],ans[maxn*maxn/10];
void Init()
{
    for(int i=0 ;i < 10000;i++)
    {
        nod[i].pre=i;
        nod[i].data=i;
        nod[i].num=1;
        nod[i].housenum=0;
        nod[i].area=0;
    }
}

int finds(int x)
{
    int r=x;
    while(r != nod[r].pre)
    {
        r=nod[r].pre;
    }
    int i=x,j;
    while(i != r)
    {
        j=nod[r].pre;
        nod[r].pre=r;
        i=j;
    }
    return r;
}
void join(int x,int y)
{
    int fx=finds(x),fy=finds(y);
    if(fx != fy)
    {
        if(fx<fy)//小的最为上级,为了最后最小的是最上级
         {
             nod[fy].pre=fx;
             nod[fx].num+=nod[fy].num;
             nod[fx].housenum+=nod[fy].housenum;
             nod[fx].area+=nod[fy].area;
         }
         else
         {
             nod[fx].pre=fy;
             nod[fy].num+=nod[fx].num;
             nod[fy].housenum+=nod[fx].housenum;
             nod[fy].area+=nod[fx].area;
         }
    }
}
bool comp(struct Node a,struct Node b)//最后结果的比较
{
    if(fabs(a.area-b.area)<(1e-7))
        return a.data<b.data;
    return a.area>b.area;
}
void output(int x)//因为在处理的时候当成了整数,为了使各个数是4位输出,
{
    if(x>=1000)
        cout<<x<<" ";
    else
        if(x>=100)
        cout<<"0"<<x<<" ";
    else if(x>=10)
        cout<<"00"<<x<<" ";
    else if(x>=0)
        cout<<"000"<<x<<" ";
}
int m[maxn][maxn],parents[maxn][maxn],x[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    Init();
    bool  flag[maxn*maxn/10];
    memset(flag,false,sizeof(flag));
    for(int i=0 ;i < n;i++)
    {
        double y;
        double z;
        scanf("%d",&x[i]);
        flag[x[i]]=true;
        scanf("%d%d",&parents[i][0],&parents[i][1]);
//        cout<<parents[i][0]<<" "<<parents[i][1]<<" ";
        flag[parents[i][0]]=true;
        flag[parents[i][1]]=true;
        scanf("%d",&m[i][0]);
//        cout<<x[i]<<" "<<parents[i][0]<<" "<<parents[i][1]<<" "<<m[i][0]<<" "<<"washdgsudfs"<<endl;
        for(int j=1;j<=m[i][0];j++)
           {
//               cout<<m[i][0]<<endl;
               scanf("%d",&m[i][j]);
//               cout<<m[i][j]<<" ";
               flag[m[i][j]] = true;
           }
        scanf("%lf%lf",&y,&z);
        nod[x[i]].housenum+=y;
        nod[x[i]].area+=z;
    }

//    for(int i=0;i<n;i++)
//    {
//        cout<<x[i]<<" "<<parents[i][0]<<" "<<parents[i][1]<<" "<<m[i][0];
//        for(int j=1;j<=m[i][0];j++)
//            cout<<m[i][j]<<" ";
//        cout<<nod[x[i]].housenum<<" "<<nod[x[i]].area<<endl;
//    }


    for(int i=0;i<n;i++)
    {
        if(parents[i][0]!= -1)
        join(x[i],parents[i][0]);
        if(parents[i][1] != -1)
        join(x[i],parents[i][1]);
        for(int j=1;j<=m[i][0];j++)
        join(x[i],m[i][j]);
    }
//    for(int i=0;i<n;i++)
//    {
//        cout<<x[i]<<" "<<parents[i][0]<<" "<<parents[i][1]<<" "<<m[i][0];
//        for(int j=1;j<=m[i][0];j++)
//            cout<<m[i][j]<<" ";
//        cout<<nod[x[i]].housenum<<" "<<nod[x[i]].area<<endl;
//    }

    int k=0;

    for(int i = 0; i <= 10000;i++)//找到最上级,然后复制到ans之中
    {
//        cout<<i<<" "<<flag[i]<<" "<<nod[i].pre<<endl;
        if(flag[i] && nod[i].pre == i)
        {

            ans[k].num=nod[i].num;
            ans[k].data=nod[i].data;
            ans[k].area=nod[i].area/ans[k].num;
            ans[k].housenum=nod[i].housenum/ans[k].num;
            k++;
        }
    }

    sort(ans,ans+k,comp);cout<<k<<endl;
    for(int i=0;i<k;i++)
    {
        output(ans[i].data);
        cout<<ans[i].num<<" ";
        printf("%.3lf %.3lf\n",ans[i].housenum,ans[i].area);
    }

    return 0;
}

POj1611 Suspect

这个题目的意思是0号是感染源,跟感染源在同一个集合里面的话就是suspect,跟suspect在同一个集合里的话也是suspect,就是要求有多少个suspect。
求与0 在同一个连通块的个数,
我的:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;

const int maxn=100000+10;

int n,m;
int pre[maxn];
int num[maxn];
void Init()
{
    for(int i=0;i<n;i++)
    {
        pre[i]=i;
        num[i]=1;
    }
}

int finds(int x)
{
    int r=x;
    while(r != pre[r])
    {
        r=pre[r];
    }
    int i=x,j;
    while(i != r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}
void join(int x,int y)
{
    int fx=finds(x),fy=finds(y);
    if(fx != fy)
    {
        pre[fx]=fy;
        num[fy]+=num[fx];
    }
}

int main()
{
    while(scanf("%d%d",&n,&m) != EOF && (n || m))
    {
        int x,y,z;
        Init();
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&x,&y);
            for(int j=0;j<x-1;j++)
            {
                scanf("%d",&z);
                join(y,z);
            }
        }
        int ans=num[finds(0)];与0在同一个连通块之中,0的最上级是finds(0),这个集合的个数就是num[finds(0)]
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值