Onenjudge 百炼 1703 ---- 发现它,抓住它 (并查集的应用)

描述
一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:
1、 D [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为

2、A [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息需要你回答[a]和[b]是否是同一个团伙所为
注意你获得信息的时间是有先后顺序的,在回答的时候只能根据已经接收到的信息做出判断。

输入
第一行是测试数据的数量T(1<=T<=20)。
每组测试数据的第一行包括两个数N和M,分别表示案件的数量和信息的数量,其后M行表示按时间顺序收到的M条信息。

输出
对于每条需要回答的信息,你需要输出一行答案。如果是同一个团伙所为,回答”In the same gang.”,如果不是,回答”In different gangs.”,如果不确定,回答”Not sure yet.”。

样例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
样例输出
Not sure yet.
In different gangs.
In the same gang.

并查集和路径压缩算法的应用。
1、 father[i] 表示 i 的父节点是谁; relation[i] 表示 i 这个节点和他父节点的关系,0表示同一个犯罪组织,1表示不同。
2、初始化将每个节点的父节点赋值为自身, relation 也理所当然的赋值为0(相同)。
3、 Find_father(int x) 函数返回值为 x 的祖先。采用路径压缩。由于是递归,途中不断将这条路上上面节点接到祖先节点,并一路修改 relation 的值,直到最下面的节点接到祖先节点。
(1)如果 x 的父节点就是祖先节点,那么 father[x] 的 r 为 0,x 的 r 不需要改变;
(2)否则,有几种情况:
·r[ f[x] ] = 1,r[x] = 1,即 x 和他父亲不同,x 父亲和 x 父亲的父亲(祖先)也不同,所以 x 和 祖先相同
·r[ f[x] ] = 1,r[x] = 0,即 x 和他父亲不同,x 父亲和 x 父亲的父亲(祖先)相同,所以 x 和 祖先不同
·r[ f[x] ] = 0,r[x] = 1,即 x 和他父亲相同,x 父亲和 x 父亲的父亲(祖先)不同,所以 x 和 祖先不同
·r[ f[x] ] = 0,r[x] = 0,即 x 和他父亲相同,x 父亲和 x 父亲的父亲(祖先)也相同,所以 x 和 祖先相同
所以总结后有这样一个公式: r[x] = (r[x] + r[ f[x] ]) % 2;
4、输入命令时,如果是 D,表示这两个案件是不同团伙所做,那么把 x 的祖先归并到 y 的祖先下面,并修改 relation的值。由于查找祖先节点时,有路径压缩过程,所以归并时 x 已经直接接到他的祖先节点上。
由于 x 和 y 的种类不同,考虑如下情况:
(1)若 y 和 y 的祖先节点 fy 相同, r[y] = 0;则 x 和 fy不同。若 r[x] = 1, x 和 fx 不同,则 fx 和 fy 相同,
即 r[fx] = 0;同理可得当 r[x] = 0,r[fx] = 1;所以 r[fx] = 1 - r[x];
(2)若 y 和 fy 不同,同上分析,可得 r[fx] = r[x].

内存: 7296kB
时间: 170ms
语言: G++
#include <iostream>
#include <cstdio>
using namespace std;

char cmd;
int T, N, M, num1, num2, fx, fy;
int father[100100], relation[100100];

int Find_father(int x)
{
    int temp = 0;
    if(x == father[x])
        return x;
    else//路径压缩
    {
        temp = Find_father(father[x]);//找到父节点的父节点--即祖先节点
        relation[x] = (relation[father[x]] + relation[x]) % 2;//修改关系
        father[x] = temp;
    }
    return father[x];
}

void Union(int x,int y)//x和y是两个不同的犯罪团伙
{
    fx = Find_father(x);
    fy = Find_father(y);
    father[fx] = fy;//将x那一支归并到y的祖先节点下
    if(relation[y] == 0)//y和fy相同
        relation[fx] = 1-relation[x];
    else
        relation[fx] = relation[x];
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);//N个案件,M条信息
        for(int i=1;i<=N;i++)
        {
            father[i] = i;//将父节点初始化为本身
            relation[i] = 0;
        }
        while(M--)
        {
            getchar();//debug时发现如果不加这个读回车,cmd就变成了回车。

            scanf("%c%d%d",&cmd,&num1,&num2);

            if(cmd == 'D')
                Union(num1,num2);
            else if(cmd == 'A')
            {
                fx = Find_father(num1);
                fy = Find_father(num2);

                if(fx != fy)//不在并查集中
                    printf("Not sure yet.\n");
                else if(relation[num1] == relation[num2])//与祖先节点关系相同
                    printf("In the same gang.\n");
                else
                    printf("In different gangs.\n");
            }
        }
    }
    return 0;
}
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值