A Bug's Life POJ - 2492 种类并查集 (两种做法)

题意

第一行输入测试组数,每组测试例的第一行输入两个整数n,m表示 昆虫数 和 交配的组数 ,后面m行每一行表示 i(昆虫的编号) 和 j 交配。判断是否有同性相交, 是输出 Suspicious bugs found!,否则输出 No suspicious bugs found! 。 

 一开始以为奇数代表一类,偶数代表一类,结果。。。太天真了   

思路

1、根据挑战程序设计里边 食物链 的做法,将 pre 数组设为原来的两倍,1~n表示一类,n+1到 2n 表示另一类。每次输入只需判断父节点是否相同即可。不相同则合并,相同则为同性交配。食物链

2、并查集的常用解法。用数组 rank 记录当前点与其父节点的关系,同性为0,异性为1。每输入一组数据判断其父节点是否相同,若不相同则合并,否则判断是否为同性。如果rank[i] 和 rank[ pre[i] ] 的值相同( 此时rank[ pre[i] ] 实际上表示的是 i 的 父亲节点与 i 的祖先节点之间的关系)都是1 或者都是 0,可以推测出此时若将 i 链接到其祖先节点的话,rank[i] 应该变为0。若值不相同,则应变为1。(公式不理解的可以自己动手写一写,比较容易推出)

法一、

#include<cstdio>
const int maxn=2505;
int pre[maxn*2];  // 1道n为一类(同一性别)  n+1到2n为另一类(同一性别)
int find(int x)
{
    return pre[x]==x?x:pre[x]=find(pre[x]);
}
void merge(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y)
        pre[x]=y;
}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        int k=0;
        scanf("%d%d",&n,&m);
        for(int j=0;j<=n*2;j++)
            pre[j]=j;
        int a,b;
        for(int j=0;j<m;j++)
        {
            scanf("%d%d",&a,&b);
            if(find(a)!=find(b))   // 不为同性
            {
                //因为不知道a b的性别  所以两种情况同时成立
                merge(a,b+n);
                merge(a+n,b);
            }
            else   //同性 
                
                k=1;
        }
        printf("Scenario #%d:\n",i);
        if(k==1)
            printf("Suspicious bugs found!\n\n");
        else
            printf("No suspicious bugs found!\n\n");
    }
    return 0;
}

法二‘

#include<cstdio>
const int maxn=2505;
int pre[maxn];  //父节点
int rank[maxn]; // 与父节点的关系
void init(int k)
{
    for(int i=0;i<=k;i++)
    {
        pre[i]=i;
        rank[i]=0;
    }
}
int find(int x)
{
    if(pre[x]==x)
        return x;

    int t=find(pre[x]);
    rank[x]=(rank[x]+rank[pre[x]])%2;  //利用函数的递归来算当前节点与其父节点的关系  从根节点往当前点算  
    pre[x]=t;

    return t;
}
void merge(int x,int y)
{
    int fx=find(x),fy=find(y);
    pre[fx]=fy;
    rank[fx]=(rank[x]+rank[y]+1)%2;  // 合并时因为两节点一定是异性(有判断条件可知),所以需要加 1 
}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        int f=0;
        scanf("%d%d",&n,&m);
        init(n);
        for(int j=0;j<m;j++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(find(a)!=find(b))
                merge(a,b);
            else
            {
                if(rank[a]==rank[b])   // 父节点相同 且与父节点关系相同则为同性
                    f=1;
            }
        }
        printf("Scenario #%d:\n",i);
        if(f==1)
            printf("Suspicious bugs found!\n\n");
        else
            printf("No suspicious bugs found!\n\n");
    }
    return 0;
}

这是我的一点见解,欢迎各位指教

猜你喜欢

转载自blog.csdn.net/qq_41837216/article/details/83041732