POJ---1703 Find them, Catch them [并查集]
Find them, Catch them
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 21732 | Accepted: 6461 |
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.
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
【转】
解析:并查集的题目,并查集的拓展。一般的思路是先初始化,各个数自成一个组,然后是两个gangs自成一个组,但由于两个给定元素有三种关系: In the same gang; In different gangs; Not sure yet; 采用此模型的缺点是判断两个元素关系还未确定这种情况比较复杂,故模型需要改进。本题的正确模型是将已经确定关系的元素组成一个集合,然后利用两个元素的 father是同一个来确定这两个元素之间的关系。father[a]中存放的是a的根结点,rank中存放的是father[a]与a的关系,0表示两者不在同一个gangs中,1表示两者在同一个gangs中。具体的程序还是沿袭了并查集的Make_Set()、Find_Set()、 Union_Set()的三步骤。
心得:并查集有三步是必须的:Make_Set()、Find_Set()、Union_Set()。 rank[a]的改变是伴随着father[a]的改变而更新的(有father改变就有rank改变),要是father改变了,而rank未改变,此时的rank就记录了一个错误的值,father未改变(即使实际的father已不是现在的值,但只要father未改变,rank 的值就是“正确”的,认识到这点很重要。),第一次错误就是因为没有考虑清楚这点。
在前面一篇文章中讲的方法比较复杂,最近有学到一种更加通用的方法,因为它还适用于1182食物链中有三种集合相关联的情况,而且此法更为简洁。
若此时告诉5号和10号属于不同派别,则6的rank值应修改为1,即rank[0]=(rank[5]+rank[10]+1)%2;
只需加一个rank数组,表示两种派别,值为0和1,初始化为0.
查询时如果两人属于同一集合并且属于派别相同就输出In the same gang.否则输出In different gangs.不属于同一集合则不能判断。
当我们要合并两个集合时,根据当前2人的派别可推出它们的祖先的派别应该怎样改变,见下图:
若此时告诉5号和10号属于不同派别,则6的rank值应修改为1,即rank[0]=(rank[5]+rank[10]+1)%2;
如果告诉4号和10号属于不同派别,则6的rank值应修改为0,即rank[6]=(rank[4]+rank[10]+1)%2;
因为在合并时并没有把子树里的每个子节点都修改完,也没有那个必要,所以在find_set时要进行修改,很简单,儿子的值要与父亲的值不同,即rank[x]=(rank[x]+rank[p[x]])%2.
code:
1 #include<iostream> 2 #include<cstdio> 3 4 using namespace std; 5 6 int father[100000+10]; //表示x的根结点 7 int rank[100000+10]; 8 9 10 void Make_Set(int n){ 11 int i; 12 for(i=1;i<=n;i++){ 13 father[i] = i; 14 rank[i] = 1; 15 } 16 } 17 18 int Find_Set(int a){ 19 if(a==father[a]) 20 return a; 21 else { 22 int temp = father[a]; 23 father[a] = Find_Set(father[a]); 24 rank[a] = (rank[temp]+rank[a]+1)%2 ; 25 //必须有,更新路径压缩之后a与根结点之间的关系; father改变,rank就必须要跟着改变 26 } 27 return father[a]; 28 } 29 30 void Union_Set(int a,int b){ 31 int fa,fb; 32 fa = Find_Set(a); 33 fb = Find_Set(b); 34 if(fa!=fb){ 35 father[fa] = fb; 36 rank[fa] = (rank[a]+rank[b])%2 ; //fa结点以下的结点的rank不需要改 37 } 38 } 39 40 int main(){ 41 //freopen("1in.txt","r",stdin); 42 //freopen("1out.txt","w",stdout); 43 int T,N,M; 44 char ch; 45 int a,b,fa,fb; 46 scanf("%d",&T); 47 while(T--){ 48 scanf("%d%d",&N,&M); 49 Make_Set(N); 50 int i; 51 for(i=1;i<=M;i++){ 52 getchar(); 53 scanf("%c%d%d",&ch,&a,&b); 54 if(ch=='A'){ 55 if(Find_Set(a)==Find_Set(b)){ 56 //在使用rank之前,已经在用Find_Set函数寻找a的时候将a路径上的所有结点的rank值改变过了 57 if((rank[a]+rank[b])%2==0){ 58 printf("In the same gang.\n"); 59 } 60 else 61 printf("In different gangs.\n"); 62 } 63 else{ 64 printf("Not sure yet.\n"); 65 } 66 67 } 68 else{ 69 Union_Set(a,b); 70 } 71 72 } 73 74 } 75 return 0; 76 }
If you have any questions about this article, welcome to leave a message on the message board.
Brad(Bowen) Xu
E-Mail : maxxbw1992@gmail.com