poj1703(并查集—“食物链”的简化版)

http://poj.org/problem?id=1703(poj1703 点击打开链接)

思路:根据是不是属于同一颗树判断是否可以确定他们的关系,然后根据关系确定是否属于同一类。

算法:经典并查集的应用。

分析:之前接触的题都是让判断是否属于同一个连通分量,这次是判断是否属于同一类,两个不同的概念。
除了需要一个数组p[]来记录父亲节点外,还需要r[]来记录当前节点与其所属连通分量的根节点的关系,r[0]表示和其根节点属于同一个帮派,r[1]表示属于不同的帮派。

开始时初始化自己是自己的父亲p[i] = i,自己和自己是同一类r[i] = 0。
一旦输入D断定x,y属于不同的集合后(即x,y关系不同),就链接x,y所在的树,同时更新r[].
一旦输入A,如果find(x) != find(y),说明还没判断过x和y,直接输出关系不确定即可,Not sure yet.
如果find(x) == find(y),如果r[x] == r[y],说明属于同一帮派,输出In the same gang.如果r[x] != r[y],说明属于不同的帮派,输出In different gangs.

注意:

  1. find函数寻找根节点的时候要不断的更新r[]
    根据子节点与父亲节点的关系,父节点与爷爷节点的关系,推导子节点与爷爷节点的关系。
    如果a和b的关系是r1,b和c的关系是r2,那么a和c的关系就是(r1+r2)%2(因为只有两种关系,要么0要么1,所以对2取余)。

    如果实在不好理解,那么我们就枚举推理一下,共有 2*2 = 4种情况:
    (a, b) (b, c) (a, c) (r1+r2)%2
    0 0 0 0 a 和 b是同类 , b 和 c 是同类, 所以 a 和 c 也是同类
    0 1 1 1 a 和 b是同类 , b 和 c 是异类, 所以 a 和 c 也是异类
    1 0 1 1 a 和 b是异类 , b 和 c 是同类, 所以 a 和 c 是异类
    1 1 0 0 a 和 b是异类 , b 和 c 是异类, 所以 a 和 c 是同类

  2. Union()联合两棵树的时候也要更新两颗树的根节点的关系.
    定义:fx是x的根节点,fy是y的根节点。

如何证明?
fx 与 x的关系时 r[x],
x 与 y 的关系是1,
y 与 fy 的关系是r[y],
所以,fx 与 fy 的关系是(r[x] + 1 + r[y]) % 2.(只有 两种关系,所以模2).

代码:

//这道题的关键是明白一个地方,属于同一棵树的不一定是一类
//同一棵树可能是不同的关系
#include 
#include 
#include 
#include 
#include 

using namespace std;
const int maxn = 100000 + 10;
int p[maxn];//存父亲节点
int r[maxn];//存与根节点的关系, 0 代表同类,1 代表不同类

int find(int x)//找根节点
{
    if(x == p[x])
        return x;
    int t = p[x];//记录父亲节点,方便下面更新r[]
    p[x] = find(t);
    r[x] = (r[x] + r[t]) % 2;//根据子节点与父亲节点的关系和父节点与爷爷节点的关系,
    //推导子节点和爷爷节点的关系
    return p[x];//容易忘记
}

void Union(int x,int y)
{
    int fx = find(x);//x所在集合的根节点
    int fy = find(y);

    p[fx] = fy;//不是同一种,合并两棵树
    r[fx] = (r[x] + 1 + r[y]) % 2;// fx与x关系 + x与y的关系 + y与fy的关系 = fx与fy的关系
}

void set(int n)
{
    for(int x = 1; x <= n; x++)
    {
        //不要有先入为主的思想,以为自己和父节点是一类
        p[x] = x;//自己是自己的父节点
        r[x] = 0;//自己和自己是同一类
    }
}

int main()
{
    int T;
    int n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%*c",&n,&m);
        set(n);

        char c;
        int x,y;
        while(m--)
        {
            scanf("%c%d%d%*c",&c,&x,&y);//注意输入!!!!!
            if(c == 'A')
            {
                if(find(x) == find(y))//如果根节点相同,则表示能判断关系
                {
                    if(r[x] != r[y])
                        printf("In different gangs.\n");
                    else
                        printf("In the same gang.\n");

                }
                else
                    printf("Not sure yet.\n");
            }
            else if(c == 'D')
            {
                Union(x,y);
            }
        }
    }
    return 0;
}

你可能感兴趣的:(并查集)