并查集总结篇

来源:互联网 发布:淘宝怎么淘到好东西 编辑:程序博客网 时间:2024/06/07 10:03

1、模板题 poj1611the suspects

每个组内的人,同一个组内都是感染者,问与“0”号人有关的有多少人

#include <iostream>#include<cstdio>using namespace std;const int MAXN = 1000100;struct DS{    int f[MAXN];    void init(int n)    {        for(int i=0;i<n;i++)            f[i]=i;    }    int ff(int x)               ///int father (a);    {        ///查找父亲并压缩路径        if(f[x]!=x)            f[x]=ff(f[x]);        return f[x];    }    void join(int a,int b)      ///void union (a, b);    {        f[ff(a)]=f[ff(b)];    }    int find(int a,int b)    {        return ff(a)==ff(b);    }}soul;int main(){   // freopen("cin.txt","r",stdin);    int n,m,a,b,c,t,num[100000];    while(cin>>n>>m)    ///n:人数    m:操作数    {        if(m==0&&n==0) break;        soul.init(n);        while(m--)        {            cin>>t;            for(int i=1;i<=t;i++) cin>>num[i];            for(int i=1;i<t;i++)             soul.join(num[i],num[i+1]);        }        int q=soul.ff(0);        int count=1;        for(int i=1;i<n;i++)         if(q==soul.ff(i)) count++;        cout<<count<<endl;    }    return 0;}

2、初级带权并查集
poj2492bug's life

题意:给出每对虫子的互相吸引关系,只有异性才会相互吸引,问虫子当中有没有同性恋==

解决方法:添加一个数组表示每个虫子的性别,注释加讲解

/* 关于并查集,注意两个概念:按秩合并、路径压缩。1、按秩合并由于并查集一般是用比较高效的树形结构来表示的,按秩合并的目的就是防止产生退化的树(也就是类似链表的树),用一个数组记录各个元素的高度(也有的记录各个元素的孩子的数目,具体看哪种能给解题带来方便),然后在合并的时候把高度小的嫁接到高度大的上面,从而防止产生退化的树。2、路径压缩而另一个数组记录各个元素的祖先,这样就防止一步步地递归查找父亲从而损失的时间。因为并查集只要搞清楚各个元素所在的集合,而区分不同的集合我们用的是代表元素(也就是树根),所以对于每个元素我们只需保存其祖先,从而区分不同的集合。而我们这道题并没有使用纯正的并查集算法,而是对其进行了扩展,我们并没有使用“1、按秩合并”(当然你可以用,那样就需要再开一个数组)我们从“1、按秩合并”得到启示,保存“秩”的数组保存的是    元素相对于父节点的关系  ,我们岂不可以利用这种关系(即相对于父节点的不同秩值来区分不同的集合),从而可以把两个集合合并成一个集合。(注:此代码 relation=0 代表 和父节点同一性别)*/#include<stdio.h>int father[2005];int relation[2005];int find_father(int i){    int t;    if(father[i]==i)        return i;    //计算相对于新的父节点(即根)的秩,relation[t]是老的父节点相对于新的父节点(即根)的秩,relation[i]是i元素相对于老的父节点的秩,    //类似于物理里的相对运动,得到的r[i]就是相对于新的父节点(即根)的秩。而且这个递归调用不会超过两层    t=father[i];        father[i]=find_father(father[i]);    relation[i]=(relation[i]+relation[t]+1)%2;   //注意递归中把这棵树relation中的的值都更新一遍,这句的顺序 不能 和上一句 调换位置    // relation[a]的改变是伴随着father[a]的改变而更新的(有father改变就有relation改变),要是father改变了,而relation未改变,此时的relation就记录了一个错误的值,    //father未改变(即使实际的father已不是现在的值,但只要father未改变,relation的值就是“正确”的,认识到这点很重要。)    return father[i];}void merge(int a,int b){    int x,y;    x=find_father(a);    y=find_father(b);    father[x]=y;    relation[x]=(relation[b]-relation[a])%2;//relation[a]+relation[x]与relation[b]相对于新的父节点必须相差1个等级,因为他们不是gay}                                            //x下边的节点不用改,因为查找的时候会自动更新int main(){    int T,m,n,i,j,a,b,flag;    scanf("%d",&T);    for(i=1;i<=T;++i)    {        flag=0;        scanf("%d%d",&n,&m);        for(j=1;j<=n;++j)       //初始化        {            father[j]=j;            relation[j]=1;        }        for(j=1;j<=m;++j)        {            scanf("%d%d",&a,&b);            if(find_father(a)==find_father(b))            {            //    if(relation[a]!=(relation[b]+1)%2)                if(relation[a]==relation[b])            //说明是同性                    flag=1;            }            else                merge(a,b);        }        if(flag)            printf("Scenario #%d:\nSuspicious bugs found!\n\n",i);        else            printf("Scenario #%d:\nNo suspicious bugs found!\n\n",i);    }    return 0;}

3、带权并查集经典题:poj1182食物链

我说不明白==参考:http://blog.csdn.net/c0de4fun/article/details/7318642

4、并查集水题 挨着就算相交,给定某个线段,询问这一团的有多少个

HDU 1558 Segment set 并查集

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int pre[1010],sum[1010];struct point{double x,y;};struct EDGE{point a,b;} edge[1010];int E;//边数int Find(int x){return x==pre[x]? x:pre[x]=Find(pre[x]);}void Merge(int a,int b){int x=Find(a),y=Find(b);if(x!=y){pre[y]=x;sum[x]+=sum[y];}}double xmult(point a,point b,point c){//大于零代表a,b,c左转return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}bool OnSegment(point a,point b,point c){//a,b,c共线时有效return c.x>=min(a.x,b.x)&&c.x<=max(a.x,b.x)&&c.y>=min(a.y,b.y)&&c.y<=max(a.y,b.y);}bool Cross(point a,point b,point c,point d){//判断ab 与cd是否相交double d1,d2,d3,d4;d1=xmult(c,d,a);d2=xmult(c,d,b);d3=xmult(a,b,c);d4=xmult(a,b,d);if(d1*d2<0&&d3*d4<0)return 1;elseif(d1==0&&OnSegment(c,d,a))return 1;elseif(d2==0&&OnSegment(c,d,b))return 1;elseif(d3==0&&OnSegment(a,b,c))return 1;elseif(d4==0&&OnSegment(a,b,d))return 1;return 0;}int main(){int i,j,k,T,n;char s[10];scanf("%d",&T);while(T--){scanf("%d",&n);E=0;for(i=1;i<=n;i++)pre[i]=i,sum[i]=1;for(i=1;i<=n;i++){scanf("%s",s);if(s[0]=='P'){E++;scanf("%lf%lf%lf%lf",&edge[E].a.x,&edge[E].a.y,&edge[E].b.x,&edge[E].b.y);for(j=1;j<E;j++)if(Find(E)!=Find(j)&&Cross(edge[E].a,edge[E].b,edge[j].a,edge[j].b))Merge(E,j);}elseif(s[0]=='Q'){scanf("%d",&k);printf("%d\n",sum[Find(k)]);}}if(T)printf("\n");}return 0;}
5、并查集浇灌农田:

HDU 1198 Farm Irrigation 并查集

给出每种小单元的上下左右联通情况,求最后整个农田分几块?把每种田地的四个边转化成数组形式,相邻的相连就用并查集

#include <iostream>#include<cstdio>#include<cstring>using namespace std;bool type[15][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}};int f[300000],n,m;char c;int num[100][100];void init(int n){    for(int i=1;i<=n;i++) f[i]=i;}int find(int x){    if(x==f[x]) return x;    int tmp=f[x]; f[x]=find(f[x]);    return f[x];}int main(){   //freopen("cin.txt","r",stdin);    while(~scanf("%d%d",&m,&n))    {        if(m==-1&&n==-1) break;        init(n*m);        for(int i=1;i<=m;i++)        {            for(int j=1;j<=n;j++)            {              cin>>c;              num[i][j]=c-65;              //cout<<num[i][j]<<" ";            }           // cout<<endl;        }        int count=n*m;        for(int i=1;i<=m;i++)            for(int j=1;j<n;j++)            {                if(type[num[i][j]][1]&&type[num[i][j+1]][3])                {                    int fx=find(i*n-n+j),fy=find(i*n-n+j+1);                    if(fx!=fy) {f[fx]=fy;count--;}                }            }        for(int i=1;i<=n;i++)            for(int j=1;j<m;j++)            {                if(type[num[j][i]][2]&&type[num[j+1][i]][0])//就是这里 这里这里~~                {                    int fx=find(j*n-n+i),fy=find(j*n+i);                    if(fx!=fy) {f[fx]=fy;count--;}                }            }        cout<<count<<endl;    }    return 0;}

6、

hdu1272小希的迷宫【并查集基础】

题意:问连接两条边是否有环,这个并查集的题用到了find函数的返回值,两个即将连接的点返回值如果相同,那么就说明要成环了!话说是不是好多图论题也可以这么搞呢?

#include <iostream>#include<cstdio>#include<cstring>using namespace std;int a[100005],x,y,count,maxn,edge,point;bool vis[100005],mark;int find(int x){    if(x!=a[x]) a[x]=find(a[x]);    return a[x];}void addto(int x,int y){    x=find(x),y=find(y);    if(x==y) {        mark=0;        return ;    }    a[y]=a[x];    return ;}void cal(){    for(int i=1;i<=maxn;i++)    {        if(vis[i])        {            if(a[i]==i) count++;           // point++;           if(count>1) mark=0;        }    }}int main(){   // freopen("cin.txt","r",stdin);    while(~scanf("%d%d",&x,&y))    {        if(x==-1&&y==-1) break;        if(x==0&&y==0)        {            printf("Yes\n");            continue;        }        memset(vis,0,sizeof(vis));        for(int i=1;i<=100000;i++) a[i]=i;        mark=1;        count=0;        maxn=0;       // point=0;        addto(x,y);        vis[x]=1;        vis[y]=1;        if(maxn<x) maxn=x;        if(maxn<y) maxn=y;         while(~scanf("%d%d",&x,&y))        {            if(x==0&&y==0) break;///            addto(x,y);            vis[x]=1;            vis[y]=1;            if(maxn<x) maxn=x;            if(maxn<y) maxn=y;        }        cal();        if(mark==1) printf("Yes\n");        else printf("No\n");    }    return 0;}

7、

hdu3461Code Lock【并查集+快速幂】

这个题的题意晦涩难懂,总的做法就是每次将两个集合合并(原来两个点就在一个集合内的不算)合并一次,一个最开始是等于长度的变量--,最后对这个变量^26取模(快速幂)

#include <iostream>#include<cstdio>#include<cstring>using namespace std;#define maxn 10000005#define mod 1000000007int f[maxn],n,m,l,r,count;bool vis[maxn];int find(int x){    if(x!=f[x]) f[x]=find(f[x]);    return f[x];}void addto(int x,int y){    x=find(x),y=find(y);    if(x==y) return ;    f[x]=y;    count++;}long long multi(int x){    long long ans=1,tmp=26;    while(x)    {        if(x&1) ans=(ans*tmp)%mod;        tmp=(tmp*tmp)%mod;        x>>=1;    }    return ans;}int main(){   // freopen("cin.txt","r",stdin);    while(~scanf("%d%d",&n,&m))    {        for(int i=1;i<=n+1;i++) f[i]=i;        count=0;        while(m--)        {            scanf("%d%d",&l,&r);            addto(l,r+1);            vis[l]=1,vis[r+1]=1;        }        /*for(int i=1;i<=n+1;i++)        {            if(vis[i]&&f[i]==i) count++;        }*/        printf("%lld\n",multi(n-count)%mod);    }    return 0;}

8、

poj3177Redundant Paths【构造双连通分量:并查集缩点 模板】

第六个题刚刚说完图论,这就来了==本题意在求出加入多少边可以构成双连通分量,而构造双连通分量的加边数=(原图的叶节点数+1)/2,用并查集缩点,枚举每个桥,[团]++,=1说明是叶子节点

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <stack>using namespace std;const int N=5006;vector<int>G[N];struct bridge{    int u,v;}bg[2*N];int vis[N],low[N],dfn[N],Time;int fa[N],deg[N];int n,m,cnt;void init(){    for(int i=0;i<n;i++) G[i].clear();    memset(dfn,0,sizeof(dfn));    memset(low,0,sizeof(low));    memset(vis,0,sizeof(vis));    memset(deg,0,sizeof(deg));    for(int i=1;i<=n;i++) fa[i]=i;    cnt=Time=0;}int findset(int x){    if(x!=fa[x])        fa[x]=findset(fa[x]);    return fa[x];}void Tarjan(int u,int father){    low[u] = dfn[u] = ++Time;    vis[u] = 1;    for(int i=0;i<G[u].size();i++)    {        int v = G[u][i];        if(v == father)            continue;        if(!vis[v])        {            Tarjan(v,u);            low[u] = min(low[u],low[v]);            if(low[v] > dfn[u])        //u->v为桥                bg[cnt].u = u,bg[cnt++].v = v;            else   //否则,u,v同属一个连通分量,合并            {                int fx = findset(u);                int fy = findset(v);                if(fx != fy)                    fa[fx] = fy;            }        }        else            low[u] = min(low[u],dfn[v]);    }}int main(){   // freopen("cin.txt","r",stdin);    while(~scanf("%d%d", &n, &m))    {        init();        for(int i=0;i<m;i++)        {            int u,v;            scanf("%d%d",&u,&v);            G[u].push_back(v);            G[v].push_back(u);        }        Tarjan(1,-1);        for(int i=0;i<cnt;i++)        {            int fx=findset(bg[i].u);            int fy=findset(bg[i].v);            deg[fx]++;            deg[fy]++;        }        int leaf=0;        for(int i=1;i<=n;i++)            if(deg[i]==1)                leaf++;        printf("%d\n",(leaf+1)/2);    }    return 0;}
9、

hdu5606 bestcoder#68 div2tree【并查集】

有一个树(<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">n</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.43056em"></span><span class="strut bottom" style="display:inline-block; height:0.43056em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord mathit" style="font-family:KaTeX_Math;font-style:italic">n</span></span></span></span></span>个点, <span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">n-1</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.72777em; vertical-align:-0.08333em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord mathit" style="font-family:KaTeX_Math;font-style:italic">n</span><span class="mbin" style="margin-left:0.22222em">−</span><span class="mord" style="margin-left:0.22222em">1</span></span></span></span></span>条边的联通图),点标号从<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">1</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.64444em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord" style="">1</span></span></span></span></span>~<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">n</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.43056em"></span><span class="strut bottom" style="display:inline-block; height:0.43056em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord mathit" style="font-family:KaTeX_Math;font-style:italic">n</span></span></span></span></span>,树的边权是<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">0</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.64444em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord" style="">0</span></span></span></span></span>或<span style=""><span class="katex" style="font-size:1.21em; font-family:KaTeX_Main; line-height:1.2; white-space:nowrap"><span class="katex-mathml" style="position:absolute; padding:0px; border:0px; height:1px; width:1px; overflow:hidden">1</span><span class="katex-html" style="display:inline-block"><span class="strut" style="display:inline-block; height:0.64444em"></span><span class="strut bottom" style="display:inline-block; height:0.64444em; vertical-align:0em"></span><span class="base textstyle uncramped" style="display:inline-block"><span class="mord" style="">1</span></span></span></span></span>.求离每个点最近的点个数(包括自己).
距离是0的。两个团连在一起,维护以某点为根节点的点的个数

#include <iostream>#include<cstdio>#include<cstring>using namespace std;int n,t,a[100005],num[100005],cnt;int fnd(int x){    if(x!=a[x]) a[x]=fnd(a[x]);    return a[x];}void addto(int x,int y){    x=fnd(x),y=fnd(y);    if(x==y) {        return ;    }    a[y]=a[x];    num[x]+=num[y];    return ;}int main(){  //  freopen("cin.txt","r",stdin);    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        cnt=0;        for(int i=1;i<=n;i++) {num[i]=1;a[i]=i;}        for(int i=1;i<n;i++)        {            int u,v,q;            scanf("%d%d%d",&u,&v,&q);            if(q==0)  addto(u,v);        }        //cout<<"44544"<<endl;        cnt=0;        for(int i=1;i<=n;i++)        {            if(a[i]==i&&(num[i]&1)) cnt^=num[i];        }        printf("%d\n",cnt);    }    return 0;}

10、

hdu3018Ant Trip【欧拉道路数量 并查集】

题意:给出一个图,问几笔画才能经过所有边 连通图有一个性质:其需要画的笔数=度数为奇数的点数除以2,那么由于给出的图并没有说明是否是连通图,我们需要用并查集来维护连通图,并且忽略单点的“子图” 所以说就和第8题一样了

#include <iostream>#include<cstdio>#include<cstring>#include<vector>using namespace std;int a[100005],deg[100005],odd[100005];bool used[100005];int fnd(int x){    if(x!=a[x]) a[x]=fnd(a[x]);    return a[x];}void addto(int x,int y){    x=fnd(x),y=fnd(y);    if(x==y) {        return ;    }    a[y]=a[x];    return ;}int main(){   // freopen("cin.txt","r",stdin);    int n,m,x,y,cnt;    while(~scanf("%d%d",&n,&m))    {        for(int i=1;i<=n;i++) {a[i]=i;deg[i]=0;used[i]=false;odd[i]=0;}        for(int i=0;i<m;i++)        {            scanf("%d%d",&x,&y);            deg[x]++;deg[y]++;            x=fnd(x);y=fnd(y);            if(x!=y)addto(x,y);        }        vector<int>v;        for(int i=1;i<=n;i++)        {            int f=fnd(i);            if(!used[f])            {                v.push_back(f);                used[f]=1;//!!!            }            if(deg[i]&1)            odd[f]++;        }        cnt=0;        for(int i=0;i<v.size();i++)        {            int k=v[i];            if(deg[k]==0) continue;            if(odd[k]==0) cnt++;            else cnt+=odd[k]/2;        }        printf("%d\n",cnt);    }    return 0;}



本来还有两个题的,离线版的lca和左偏堆,

hdu2586How far away ?【LCA tarjan求最短距离】

hdu1512 Monkey King【左偏堆、并查集】

和并查集的关系不大,就没写


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 怀孕6个月了咳嗽怎么办 怀孕了咳嗽黄痰怎么办 科二5次没考过怎么办 交警开的罚单丢了怎么办 驾驶员从业资格证被水洗了怎么办 驾照考五次没过怎么办 学车每次考不过怎么办 年检超期3个月怎么办 年检超过3个月怎么办 工商执照年检时候忘记密码怎么办 个体工商营业执照年检过期怎么办 忘了审车时间怎么办 汽车年检超过时间了怎么办 星巴克金星会员到期怎么办 驾考学员证丢了怎么办 违章扣满12分怎么办 违章扣满12分后怎么办 东方时尚驾校科二考不过怎么办 东方时尚科二科三考五次没过怎么办 不想考科目三了怎么办 科目三两次没过怎么办 练科目三很紧张怎么办 驾照学员卡丢了怎么办 驾考时考试的车系统出错怎么办 驾驶证超期6个月怎么办 北京汽车年检只有电子保单怎么办 交电费户号9位数怎么办 扬州驾照12分扣完了怎么办 有大专毕业证在深圳怎么办居住证 微信解释包错误怎么办 富士康离职不批怎么办 到了怀孕年龄找不到工作怎么办 建筑公司挂靠发生人员伤残怎么办 外地生小孩落北京户口怎么办 户籍档案查不到直系亲属关系怎么办 考过了二建注册怎么办 异地工作辞职回家档案怎么办 离职怎么办档案放在人才市场 人才房住了6年后怎么办 医保辞职后断了怎么办 社保断了生育险怎么办