POJ 1703 Find them, Catch them【典型并查集:判断在不同的集合】

76 篇文章 0 订阅
21 篇文章 0 订阅
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 23915 Accepted: 7167

Description

The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.) 

Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds: 

1. D [a] [b] 
where [a] and [b] are the numbers of two criminals, and they belong to different gangs. 

2. A [a] [b] 
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang. 

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.

Output

For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."

Sample Input

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

Sample Output

Not sure yet.
In different gangs.
In the same gang.

Source


题意 :有两个不同的帮派,每个帮派至少有一个人。 判断两个人是否属于同一个帮派。
              有 T 组测试数据。
              给你 N 个人,编号从 1 到 N,操作 M 次。
              每次操作输入一个字符和两个数 x ,y 
              如果字符为 A 则判断 x 和 y 是否属于同一个帮派,并且输出结果。
              如果字符为 D 则明确告诉你 x 和 y 是属于不同帮派的。

算法:经典并查集的应用。
PS:以前接触的并查集都是让我们判断是否属于同一个连通分量,但这道题却让你判断是否属于同一类。
        开始小纠结了下,如果我昨天没有纠结清楚 POJ 1182 食物链 那题想必肯定是做不出来的,其实想清楚了,这道题就是 食物链 
        那题的简单版本。大笑

思路:除了像普通的并查集定义一个 p[] 记录父亲节点外,还定义一个 r[] 记录当前点与其所属的连通分量的根节点的关系。
            r[] = 0 表示属于同一个帮派; r[] = 1表示与其根节点属于不同的帮派。
            
           开始时初始化自己是自己的父亲 p[x] = x,自己与自己属于同一类 r[x] = 0.
           一旦输入 D 断定 x 和 y 属于不同集合后,就连接 x 和 y 所在的树,同时更新 r[]
           一旦输入 A
           如果 find(x) 不等于 find(y) 说明还没有判断过 x 与 y 直接输出关系不确定即可
      Not sure yet.
      如果find(x)等于 find(y) ,但是他们的r不等,说明属于不同帮派,输出In different gangs.
                               如果他们的r相等,说明属于同一个帮派,则输出In the same gang
 
 
注意:1.find()函数寻找根节点的时候要不断的更新 r  
       根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
       如果 a 和 b 的关系是 r1, b 和 c 的关系是 r2,
       那么 a 和 c 的关系就是 (r1+r2)%2 . PS:因为只用两种情况所以对 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 的根节点
       联合时,使得 p[fx] = fy; 同时也要寻找 fx 与 fy 的关系。关系为:(r[x]+r[y]+1)%2
       如何证明?
       fx 与 x 的关系是 r[x], 
       x 与 y 的关系是 1 (因为确定是不同类,才联合的), 
       y 与 fy 关系是 r[y],模 2 是因为只有两种关系
       所以又上面的一点所推出的定理可以证明 fx 与 fy 的关系是: (r[x]+r[y]+1)%2
 
 
/*
D
Accepted
916 KB
329 ms
C++
1120 B
2013-04-08 18:29:33
*/
#include<cstdio>

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(p[x]);
    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); //注意输入
            //printf("%c\n", c);
            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;
}


  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值