您的位置:首页 > 其它

POJ-1703-Find them, Catch them 解题报告

2014-05-24 13:11 148 查看
一道标准的关系型并查集题。普通的并查集是给几个同类的元素,而关系型并查集是给不同类的元素,然后求各个元素之间的关系。

题目大意是:在一个城市里有两种不同的犯罪团伙。首先输入T表示有T组测试,然后输入N和M,表示有N个罪犯(编号从1到N)而且接下来有M个操作。操作分为两种:

1.D a b,表示编号为a和b的两个罪犯属于不同的犯罪团伙;

2.A a b,表示询问编号为a和b的两个罪犯是否是同一个犯罪团伙或者不确定。

对于每一个A操作,根据题意都要有相应的回答(输出)。

接下来是解题思路:既然是用并查集来实现,我们可以定义一个整型数组表示当前节点与父节点的关系,偶数代表同类,奇数代表异类。初始化时每一个节点的父节点都为自己,关系数都为0。

每次进行查找一个节点的根节点时,都应该进行路径压缩,将沿途节点的父节点都修改为根节点,而关系数也应该对应地修改为与根节点的关系。该节点与根节点的关系数为该节点到根节点沿途所有节点的关系数之和(不包括该节点与当前父节点的关系数)。

进行合并操作时我们知道需要合并的两个原节点的关系为异类,如果这两个原节点的根节点相同(也就是说在同一个集合里面),那么我们可以直接忽略合并操作;如果这两个原节点的根节点不相同,那么应该对这两个原节点的根节点进行合并,合并后应该满足这两个原节点的关系为异类,我们需要并且只需要更新拥有新根的原根节点信息即可

。合并方式为将其中一个根节点的父节点修改为另一个根节点,而该根节点的关系数则应修改为两个原节点与原根节点的关系数之和加1,因为已知的信息是两个原节点的关系为异类,这样的操作是根据两个原节点之间关系以及两个原节点与对应根节点之间的关系得来的。

下面是解题代码
#include <stdio.h>
#define N 100001

int bleg
;    //存储父节点
int rela
;    //存储与父节点的关系,偶数为同类,奇数异类
int t;
int n, m;
char *ns = "Not sure yet.";
char *dg = "In different gangs.";
char *sg = "In the same gang.";

void Init();        //初始化

int Find(int x);    //并查集查找

void Union(int x, int y);   //并查集合并

int main()
{
    char a;
    int x, y;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d %d", &n, &m);
        Init();           
        while (m--)
        {
            scanf(" %c %d %d", &a, &x, &y);
            if (a == 'D')
            {
                Union(x, y);
            }
            else
            {
                if (Find(x) != Find(y))     //如果两节点的根节点不相同,则无法知道两节点的关系
                {
                    puts(ns);
                }
                else if ((rela[x] + rela[y]) % 2 == 0)  //推论可得,表示两节点的关系数为两节点与共同的根结点的关系数之和
                {
                    puts(sg);
                }
                else
                {
                    puts(dg);
                }
            }
        }
    }
    return 0;
}

void Init()     //初始化
{
    int i;
    for (i=0; i<N; i++)
    {
        bleg[i] = i;
        rela[i] = 0;
    }
    return;
}

int Find(int x)         //并查集查找
{
    int y = bleg[x];
    int sum = rela[x];
    int temp, z;
    while (y != bleg[y])    //当y不是根节点时
    {
        sum += rela[y];
        y = bleg[y];
    }
    while (x != bleg[x])    //路径压缩并将沿途所有节点的关系改成与根节点的关系
    {
        z = bleg[x];
        temp = rela[x];
        rela[x] = sum;
        bleg[x] = y;
        sum -= temp;
        x = z;
    }
    return y;
}

void Union(int x, int y)    //并查集合并
{
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy) return;
    bleg[fx] = fy;
    rela[fx] = rela[x] + rela[y] + 1;
    return;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: