POJ 1703--Find them, Catch them

题意:一个城市有两个犯罪团伙,给定两种输入,第一种输入表示a,b两个人不在同一个团伙,第二种输入询问a,b是否是同一个团伙,输出到上一个输入为止能知道的他们的关系。

题解:并查集的一种扩展应用,原始的并查集集合中元素代表他们属于同一种集合,本题可以用这种集合代表有关系的一种集合,而集合类元素之间的关系则可以通过他们分别与根节点的关系求得。

  1. 关系转移:设IsSame[a] = 0代表a与他的根节点fa[a]属于同一团伙,等于1代表a与根节点不属于同一团伙,则若fa[a] == fa[b],a,b之间的关系:IsSame[a] ^ IsSame[b],同样为0代表a,b属于同一团伙,为1代表a,b属于不同团伙。
  2. 路径压缩:并查集的一种优化,在搜索a的根节点时直接将路径中的点指向最终的根节点,同样关系表IsSame[a]也需要随着根节点变化而变化。
注意:题意要求每个团伙至少有一个人,则当输入为两个人时,不需要任何其他输入我们就已经知道两个人属于不同团伙。而三个以上人,当没有关系提示时,我们不知道他们的关系,而当一有关系输入时,题设的条件已经能满足了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

class solve
{
private:
    int N,M;
    int* fa;
    int* height;
    char* IsSame;       //1表示点与根节点不同伙,0表示同伙
    char c;
    int a,b;
public:
    solve(int n,int m):N(n),M(m)
    {
        fa = new int[N+1];
        height = new int[N+1];
        IsSame = new char[N+1];
        makeSet();
        if(n == 2)      //每个犯罪团伙至少有一人
        {
            unionSet(1,2);
        }
        while(M--)
        {
            getchar();
            c = getchar();
            scanf("%d%d",&a,&b);
            if(c == 'D')
            {
                unionSet(a,b);
            }
            else
            {
                if(findSet(a) == findSet(b))
                {
                    if(IsSame[a]^IsSame[b])
                    {
                        printf("In different gangs.\n");
                    }
                    else
                    {
                        printf("In the same gang.\n");
                    }
                }
                else
                {
                    printf("Not sure yet.\n");
                }
            }
        }
        return ;
    }
    ~solve()
    {
        delete[] fa;
        delete[] height;
        delete[] IsSame;
    }
    int makeSet();
    int findSet(int x);
    int unionSet(int x,int y);
};

int solve::findSet(int x)
{
    if(x == fa[x])
        return x;
    int tmpfa = fa[x];
    fa[x] = findSet(fa[x]);
    IsSame[x] ^= IsSame[tmpfa]; //关系表随着根节点变化而变化
    return fa[x];
}

int solve::unionSet(int x,int y)
{
    int rootX = findSet(x);
    int rootY = findSet(y);
    if(rootX != rootY)     //根节点不同则开始合并
    {
        if(height[rootX] > height[rootY])   //按树的深度大小进行合并
        {
            fa[rootY] = rootX;
            IsSame[rootY] = IsSame[x]^IsSame[y]^1;      //因为输入的x,y关系为1,经过x,y求得rootX和rootY的关系
        }
        else
        {
            fa[rootX] = rootY;
            IsSame[rootX] = IsSame[x]^IsSame[y]^1;
            if(height[rootX] == height[rootY])
                height[rootY]++;
        }
    }
    return 0;
}

int solve::makeSet()
{
    memset(IsSame,0,sizeof(char)*(N+1));    //初始化所有人与自己同伙
    memset(height,0,sizeof(int)*(N+1));
    for(int i = 1;i <= N;i++)
    {
        fa[i] = i;      //初始化每个人和自己一组
    }
    return 0;
}

int main()
{
    int T;
    int n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        solve poj_1703(n,m);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值