HDU 1829 && POJ 2492 A Bug's Life(种类并查集)

题目地址:HDU 1829     POJ 2492

这个题可以用两种方法做,第一眼看完题是觉得用dfs染色判断二分图。然后又写的刚学的种类并查集。原来并查集可以这样用,真是神奇。。

dfs染色代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include <set>
#include <algorithm>

using namespace std;
int vis[3000], head[3000], cnt, flag;
struct node
{
    int u, v, next;
} edge[2010000];
void add(int u, int v)
{
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u)
{
    int i;
    if(flag) return ;
    for(i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if(!vis[v])
        {
            vis[v]=-vis[u];
            dfs(v);
        }
        else
        {
            if(vis[v]==vis[u])
            {
                flag=1;
                return ;
            }
        }
    }
}
int main()
{
    int n, m, i, t, u, v, num=0;
    scanf("%d",&t);
    while(t--)
    {
        num++;
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        cnt=0;
        while(m--)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        memset(vis,0,sizeof(vis));
        flag=0;
        for(i=1; i<=n; i++)
        {
            if(!vis[i])
            {
                vis[i]=1;
                dfs(i);
            }
        }
        printf("Scenario #%d:\n",num);
        if(flag)
            puts("Suspicious bugs found!\n");
        else
            puts("No suspicious bugs found!\n");
    }
    return 0;
}

       下面一种方法是种类并查集。终于明白原理是为什么了。

      种类并查集是给每个结点一个权值。然后在合并和查找的时候根据情况对权值来进行维护。

      对于这个题来说,就可以给每个节点设置一个0或1的权值。0代表与根节点同性,1代表与根节点异性。

      与根节点的关系可以在查找的时候利用递归从根节点向叶子不断进行维护。利用抵消的原理,不断相加余2就行了。

      然后合并的时候,由于根节点的性别是不确定的,不能简单简单的合并就完了,还要考虑两颗树的根节点之间的关系。这时候,要想使两个异性结点一个为0一个为1,只要将被合并的那棵树的根节点维护一下就可以,下面的所有的结点的值都可以在查找的时候由这个根节点来决定。因为,如果根节点为0,那么下面的都不会改变,如果为1的话,那下面的都会改变。剩下的就是考虑这个根节点要怎么确定。

     要想使输入两个异性结点一个为0一个为1,假如此时在两颗树中显示的是同性,那么只要把根节点设置成1,否则,就设置成0.这地方应该很好想,就不解释了。这时可以用两者权值之和加1余2来实现。

代码如下:

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <queue>
#include <map>
#include <set>
#include <algorithm>

using namespace std;
int bin[3000], rank[3000];
int find1(int x)
{
    int y;
    if(bin[x]!=x)
    {
        y=bin[x];
        bin[x]=find1(bin[x]);
        rank[x]=(rank[y]+rank[x])%2;
    }
    return bin[x];
}
int join(int x, int y)
{
    int f1=find1(x);
    int f2=find1(y);
    if(f1==f2)
    {
        if(rank[x]==rank[y])
            return 1;
    }
    else
    {
        bin[f2]=f1;
        rank[f2]=(rank[x]+rank[y]+1)%2;
    }
    return 0;
}
int main()
{
    int n, m, i, t, flag, num=0, a, b;
    scanf("%d",&t);
    while(t--)
    {
        num++;
        flag=0;
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {
            bin[i]=i;
            rank[i]=0;
        }
        while(m--)
        {
            scanf("%d%d",&a,&b);
            if(flag) continue ;
            if(join(a,b))
                flag=1;
        }
        printf("Scenario #%d:\n",num);
        if(flag)
            puts("Suspicious bugs found!\n");
        else
            puts("No suspicious bugs found!\n");
    }
    return 0;
}


你可能感兴趣的:(编程,算法,C语言,ACM,并查集)