并查集学习入门到熟悉

来源:互联网 发布:oracle 数据库高可用 编辑:程序博客网 时间:2024/05/01 21:31

我的:
并查集就是把具有相同属性的一些事物归为一类,然后归类之后后就相当于形成了多个联通块,其中要注意的是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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 高中生掉发严重怎么办该看什么科 脱头发如何治疗 掉头发厉害怎么办 头发上突然秃了一小块怎么办 全秃过后长出来白色头发怎么办 高三学生喜欢打球影响学习怎么办 高三学生受同学搔挠学习怎么办 烫完头发掉头发很厉害怎么办 烫头发后掉头发很厉害怎么办 十八岁了下门牙活动疼痛怎么办 烫完头发后掉头发很厉害怎么办 16岁的孩孑得了肺炎怎么办 我的头发掉的很厉害怎么办 八个月宝宝头后面没头发怎么办 生完孩子掉头发很厉害怎么办 生完孩子后掉头发很厉害怎么办 头发又细又软又卷怎么办 后颈部没有头发掉光了想植发怎么办 头发掉的厉害怎么办怎么拯救掉头发 头发可以种植吗 如果是秃顶怎么办 染头发把手指甲染黑了怎么办 怀孕两个月下体流褐色分泌物怎么办 头发总是大把大把的得掉 怎么办 严重脱发怎么办去问南宁肤康 脱发严重怎么办去看南宁肤康 前额头发少怎么办 如何使头发增多 生完宝宝头发一把一把的掉怎么办 生完宝宝后头发掉的厉害怎么办 生完宝宝头发掉的厉害怎么办 生了小孩后头发掉很多怎么办 生了孩子头发掉的很厉害怎么办 母乳期头发掉的很厉害怎么办 宝宝吃母乳头发掉的厉害怎么办 头发油腻头皮屑多还掉头发怎么办 头发剪了中分刘海弯了怎么办 头发掉了长出来的头发很细怎么办? 头皮损伤毛囊怎么办还会长头发吗 一岁宝宝头发稀少怎么办能刮光头么 前编头发长了怎么办怎么梳理 九个月宝宝头发稀少不长怎么办 前牙吃饭咬合很深吃饭就痛怎么办 吃了甜的冷的就牙疼怎么办