并查集入门题集 POJ1611 POJ1703 POJ2524 POJ1182

这几道题都是POJ中入门级别的并查集题目……一一道来


http://blog.csdn.net/dellaserss/article/details/7724401这个写的特别好,基本上开完就能懂并查集的基本思想。


POJ1161 The Suspects

题意说是传染SARS其实就是找组织门派的……给出两个关系在一组的话,比较它们的find是否相等,相等不管了,不相等合并到一起并进行处理就好了……

Code:

#include<iostream>
using namespace std;
int pre[30000];
int a[30001];
int find(int x)
{
    if(pre[x]==x)
        return x;
    else
    {
        return find(pre[x]);
    }
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        if(n==0&&m==0)break;
        for(int i=0; i<n; i++)
            pre[i]=i;

        for(int i=1; i<=m; i++)
        {
            int k;
            cin>>k;
            for(int j=1; j<=k; j++)
                cin>>a[j];

            for(int jj=2; jj<=k; jj++)
            {
                if(find(a[jj])!=find(a[1]))
                {
                    int temp=find(a[jj]);
                    pre[temp]=a[1];
                }
            }
        }
    int total=0;
    for(int i=0; i<n; i++)
    {
        if(find(i)==find(0))
            total++;
    }
    cout<<total<<endl;
}
}

POJ1703   Find them, Catch them

题意:有两个帮派,各有一些成员,现在我们要区分它们的关系,在一个帮派,不在一个帮派或者不确定……

很容易想到并查集,但这道题目其实是一道种类并查集。我们不管要将所有的人放到一个中区,我们还需要记录每个点和find()(就是唯一的pre[x]==x的那个点,也就是文章中说的掌门)的关系,开了一个数组w,和他一个帮派记录0,不一个记录1。 种类并查集往往需要通过关系计算w,,w[b]=(w[x]+w[y]+1)%2;这个式子就是这道题的公式。

我们针对询问,两个x,y如果find(x)!=find(y)他们的关系还没有被确定。 反之比较w,如果w相等为一个帮派,反之为不同帮派的。


AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int pre[100001];
int w[100001];
int find(int x)
{
    int temp;
    if(pre[x]==x)return x;
    else
    {
        temp=pre[x];
        while(pre[temp]!=temp)
        {
            w[x]=(w[x]+w[temp])%2;
            temp=pre[temp];
        }
        pre[x]=temp;                            //路径压缩
    }
    return temp;
}
void Union(int x,int y)
{
    int a=find(x);
    int b=find(y);
    if(a!=b)
    {
        pre[b]=a;
        w[b]=(w[x]+w[y]+1)%2;
    }
}
int main()
{
    int T;
     scanf("%d",&T);
    while(T--)
    {
        memset(w,0,sizeof(w));
        int n,m,t1,t2;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            w[i]=0;
            pre[i]=i;
        }

        char c;
        for(int i=1; i<=m; i++)
        {    getchar();
            scanf("%c %d %d",&c,&t1,&t2);
            if(c=='A')
            {
                int x1=find(t1),x2=find(t2);
                if(x1!=x2)
                    printf("Not sure yet.\n");
                else
                {
                    if(w[t1]==w[t2])
                        printf("In the same gang.\n");
                    else
                        printf("In different gangs.\n");
                }
            }
            else
            {
                Union(t1,t2);
            }
        }
    }
}

POJ2524 Ubiquitous Religions

题目意思很简单,找不同宗教信仰……这题目没什么难度啦,大家练练手,不多说了。

AC code:

#include<iostream>
#include<cstdio>
int pre[50001];
int rank[50001];
int num[50001];
int total;
using namespace std;
int find(int x)
{
    if(pre[x]==x)return x;
    else
        return find(pre[x]);
}
void Union(int x,int y)
{
    if(num[x]<num[y])
    {
        num[y]+=num[x];
        pre[x]=y;
        total-=1;
    }
    else
    {
        num[x]+=num[y];
        pre[y]=x;
        total-=1;
    }
}
int main()
{
    int n,m,T=0;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        T++;
        total=n;
        int a,b;
        for(int i=1; i<=n; i++)
        {
            pre[i]=i;
            num[i]=1;
        }
        for(int i=1; i<=m; i++)
        {
           scanf("%d %d",&a,&b);
            int x=find(a);
            int y=find(b);
            if(x!=y)
            {
              Union(x,y);
            }
        }
        printf("Case %d: %d\n",T,total);

    }
}


POJ1182 食物链

非常经典的种类并查集。

和POJ1703很像,但是关键是有三种类型,公式可能比较难发现。思路还是把所有的不同类鱼放到一个关系中,在记录和首的关系……0同类,1吃他,2被吃。公式是关键。

Code:

#include<iostream>
#include<cstdio>
using namespace std;
int pre[50001];
int rank[50001];
int sum;
int find(int x)
{    int temp;
    if(pre[x]==x)return x;
    else
    {
        temp=pre[x];
        while(pre[temp]!=temp)
        {
            rank[x]=(rank[x]+rank[temp])%3;
            temp=pre[temp];
        }
        pre[x]=temp;                            //路径压缩
    }
    return temp;
}
void Union(int x,int y,int c,int i)
{
    int a=find(x);
    int b=find(y);
    if(a==b)
    {
        if((rank[y]-rank[x]+3)%3!=c-1)
        {
            sum++;
        }
    }
    else
    {
        pre[b]=a;
        rank[b]=(rank[x]-rank[y]+3+c-1)%3;       //转化公式
    }
}
int main()
{
    int n,k;
    scanf("%d %d",&n,&k);
    sum=0;
    int a,b,c;
    for(int i=1; i<=n; i++)
    {
        pre[i]=i;
        rank[i]=0;
    }
    for(int i=1; i<=k; i++)
    {
        scanf("%d %d %d",&c,&a,&b);
        if(a>n||b>n)
        {
            sum++;
        }
        else if(a==b&&c==2)
        {
            sum++;
        }
        else
        {
            Union(a,b,c,i);
        }
    }
    printf("%d\n",sum);

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值