POJ1703 Find them, Catch them(关系并查集)

本文介绍了一种基于并查集的算法,用于判断两个罪犯是否属于同一帮派。算法通过处理一系列操作,包括定义不同帮派的关系和询问两个罪犯的关系,能够准确判断罪犯所属帮派情况。

题目来源:http://poj.org/problem?id=1703

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.

题意:

给你一些操作数:“字母A或者D” 数字a和b

A表示询问a和b关系,如果是同一类就输出“In the same gang.”,如果不是同一类就输出“In different gangs.”,不确定这输出“Not sure yet.”

思路:

类似于食物链

1:只要维护不用种类之间的关系即可,如果a和b不是一类就让a和b+n是一类,b和a+n是一类,查询是候直接判断就好(详细看代码)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include<vector>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define ll long long
#define N 100005
#define same(u,v) Find(u)==Find(v)

using namespace std;
int pre[N<<1],re[N<<1];
int T,n,m,a,b;
char c;
void init()//初始化
{
    for(int i=1; i<=2*n; i++)
    {
        pre[i]=i;
        re[i]=0;
    }
}
int Find(int x)
{
    return x==pre[x]? x : pre[x]=Find(pre[x]);
}
void Join (int u, int v)
{
    int fu,fv;
    fu=Find(u);
    fv=Find(v);
    if(fu==fv)
        return;
    if(re[fv]>re[fu])
        pre[fu]=fv;
    else
    {
        pre[fv]=fu;
        if(re[fu]==re[fv])
            re[fu]++;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        init();
        for(int i=0; i<m; i++)
        {
            getchar();
            scanf("%c %d %d",&c,&a,&b);
            if(c=='D')//a和b不是一类让a和b+n是一类,b和a+n是一类
            {
                Join(a,b+n);
                Join(a+n,b);
            }
            else
            {
                if(same(a,b))
                    printf("In the same gang.\n");
                else if(same(a,b+n))
                    printf("In different gangs.\n");
                else
                    printf("Not sure yet.\n");
            }
        }
    }
    return 0;
}

2:处理种类直接的关系:将两个集合的所有及节点合并到一个并查集中,通过数组re表示与根节点或者父节点是否相同

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include<vector>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
#define N 100005
#define same(u,v) Find(u)==Find(v)
using namespace std;
int pre[N<<1],re[N<<1];                // re=1表示与父节点不属于同一集合
int T,n,m,a,b;
char c;
void init()
{
    for(int i=1; i<=2*n; i++)
    {
        pre[i]=i;
        re[i]=0;
    }
}
int Find(int x)
{
    if(pre[x]==x)
        return x;
    else
    {
        int temp=Find(pre[x]);                     //先要从子节点向根节点搜索,进入最里面的节点,寻找根节点
        re[x]=(re[pre[x]]+re[x])%2;   //此处很重要,是通过根节点与父节点(爷与父)的关系和这个节点与父节点原来状态的关系(是否是同一集合的问题)求这个节点现在的状态,也就是与将要连到根节点的关系(异或关系)
        return pre[x]=temp;                     //关系确定后再将路径压缩,子节点连入根节点(此时rel的值也缺定了)
    }           //顺序不可变,先更新re值,再连接点根节点上
}
void Join(int x,int y)
{
    int fx=Find(x);
    int fy=Find(y);
    pre[fx]=fy;                                  //直接结合只要是D指令,将必定不是同一集合,将树中的两个数分在不同集合
    if (re[y]==0)                              //且fx的父节点不是y而是y的根节点
        re[fx]=1-re[x];                    //如果fy将成为fx的父节点,如果他将通过该x与原来根节点的关系re[x]和现在新建成的根节点y和x(此时属于不同集合)得到原来根节点的值,也就是与现在根节点fy的关系(同时,他正确的情况下可以通过find将所以的值rel正确)
    else re[fx]=re[x];                  //此时 的情况属于x和y了不同集合,y和fy不同集合(fy也就是其根节点也是父节点)那么x和fy同一集合,根节点的re一定为0,那么re[X]与原根节点是否同一集合就是re[fx]
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        init();
        for(int i=1; i<=m; i++)
        {
            getchar();
            scanf(" %c %d %d",&c,&a,&b);
            if(c=='A')
            {
                if(same(a,b)!=1)         //这里面包含Find函数,所以re值正确,为后面判断做铺垫,做不在这个树内则无法判断
                    printf("Not sure yet.\n");
                else if(re[a]==re[b])  //这个表示与根节点的相同与否,也就是否是一个集合
                    printf("In the same gang.\n");
                else
                    printf("In different gangs.\n");
            }
            else
                Join(a,b);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuoyanli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值