发现它,抓住它--并查集,偏序关系

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

  1. 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.

思路:
和“食物链”poj 1182 这道题类似,只是这里的relation只有两种:
relation = 0 : 相同
relation = 1 : 不同

1) 用struct Crime来代表每一个动物, 作为并查集中的元素。凡是知道相互关系的元素都放在一个集合里面

struct Crime{
int parent;
int num;
int relation;
} Crime[100010];

2) 在一个集合中的元素相互关联,如果a,b有关系,b,c有关系,那么a,c之间的关系式可以通过ab、bc的关系推出来的。
node 和 node 的parent (node-parent)有2种关系:
(0) relation = 0: node 和 parent 属于同一个gang
(1) relation = 1: node 和 parent 属于不同的gang

node – parent – root 的关系三者关系的推算:
Node –> root 的 relation = ( Node–> parent+ parent –>root) % 2

3) 压缩: 每一个元素的parent都设置成第一个元素root,并且保存和第一个元素的relation

4) 合并:

x的root是rootA, y的root是rootB,并且知道x与y是属于不同的gang, 现在把b的root设置成a:

已知:x–>rootA, y–>rootB, x–>y:
roots–>roota = (b–>y + y–>x +1)%2

***** 需要注意的几个逻辑点 ******
1. 在初始Crime结点的时候,要把parent设置成自己,并且relation的标记设为SAME
2. 判断not sure: 需要判断的两个结点不在一个集合里面,则不能为not sure
3. 判断的时候(A语句的时候),也要用find(&crime)来把这个节点的parent设置成为root. 因为union()函数并没有把rootb的子结点的parent设置成roota


#include <iostream>
using namespace std;

#define SAME 0
#define DIFF 1
#define NOTSURE 2

struct crime{

    int parent; //如果node是root, 即node的parent是自己,储存自己的序号
    int num;   //node的序号
    int relation; // 记录和root/parent的关系
} Crime[100010];

int find(crime* Node){
    if (Node->parent==Node->num) return Node->num; // 找到了root

    int temp = Node->parent;

    Node->parent = find(&Crime[temp]);// 把路径上的每一个点的parent都设置成为了root
    Node->relation = (Node->relation + Crime[temp].relation)%2;
    // 把路径上的每一个点的relation都设置成为了和root的relation
    return Node->parent;
}

void Union(int X, int Y, int ROOTA, int ROOTB){

    // 合并的时候不用压缩, 即rootb的所有子结点的parent仍然是rootb 。 后面在判断句子的正误的时候,会先用find函数找root,那时会循环设置parent成为roota

    Crime[ROOTB].parent = ROOTA;
    Crime[ROOTB].relation =(Crime[Y].relation + Crime[X].relation+1)%2;


}
void Init(int n){

    for (int i=0; i<=n; i++){ // 不要忘了循环到n, 而不是n-1
       Crime[i].num=i;
       Crime[i].parent = i;
       Crime[i].relation = SAME;

    }
}

int main(){
    int T;
    cin>>T;
    while (T--){
        int N,M;
    cin>>N>>M;

    Init(N);

        char AD;
        int X,Y;

    while(M--){

        cin>>AD>>X>>Y;


            int rootA = find(&Crime[X]); // 从X到root的路线都“打通了”
            int rootB = find(&Crime[Y]); // 从X到root的路线都“打通了”

        if (AD == 'D'){
            if (rootA != rootB)
                Union (X,Y,rootA, rootB);
        }
        else if (AD=='A'){


            if (rootA==rootB && Crime[Y].relation == Crime[X].relation)
                cout<<"In the same gang."<<endl;
            else if (rootA==rootB && Crime[Y].relation != Crime[X].relation)
                cout<<"In different gangs."<<endl;
            else if (rootA !=rootB )
                cout<<"Not sure yet."<<endl;
        }
    }
    }
    return 0;
}
    原文作者:犯罪团伙问题
    原文地址: https://blog.csdn.net/melanieecd/article/details/77046731
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞