POJ 1703 Find them, Catch them 并查集的应用
题意:城市中有两个帮派,输入中有情报和询问。情报会告知哪两个人是对立帮派中的人。询问会问具体某两个人的关系。
思路:并查集的应用。首先,将每一个情报中的两人加入并查集,在询问时先判断一下两人是否在一个集合中,如果是,则表明两个人的关系已知。
本题还需要判断出两人是不是在同一帮派,这里用一个relation数组来维护每个人与根结点的关系。
0表示该人与根节点为同一个帮派。
1表示该人与根节点为对立帮派。
并查集有两个基本操作:find——查找根节点, merge——合并两个集合。
在合并两个集合时,其中一个集合的relation的值要发生变化。在这里只需要改变集合根节点的relation(改变的规则见代码中merge函数,relation改变的规则自己举几个例子就明白了)。
其余点的值在find函数压缩路径时就顺便完成了。relation的状态只有两种(0和1),在压缩路径时的变化规则就是点到根节点的relation=(与父亲结点的relation+父亲结点与根节点的relation)% 2。
1 #include<stdio.h> 2 #define maxn 100010 3 int father[maxn]; 4 bool relation[maxn]; 5 int Find(int x) 6 { 7 if (father[x] != x) 8 { 9 int t = father[x]; 10 father[x] = Find(father[x]); 11 relation[x] = (relation[t] + relation[x]) % 2; 12 } 13 return father[x]; 14 } 15 void Merge(int x,int y) 16 { 17 int a = Find(x); 18 int b = Find(y); 19 father[a] = b; 20 if (relation[y] == 0) 21 relation[a] = 1 ^ relation[x]; 22 else relation[a] = relation[x]; 23 } 24 int main() 25 { 26 int t, n, m; 27 scanf("%d",&t); 28 while (t--) 29 { 30 scanf("%d%d",&n,&m); 31 for (int i = 1; i <= n; i++) 32 { 33 father[i] = i; 34 relation[i] = 0; 35 } 36 char op; 37 int a, b; 38 while (m--) 39 { 40 getchar(); 41 scanf("%c",&op); 42 if (op == 'D') 43 { 44 scanf("%d%d",&a,&b); 45 Merge(a, b); 46 } 47 else 48 { 49 scanf("%d%d",&a,&b); 50 if (Find(a) == Find(b)) 51 { 52 if (relation[a] == relation[b]) 53 printf("In the same gang.\n"); 54 else 55 printf("In different gangs.\n"); 56 } 57 else 58 printf("Not sure yet.\n"); 59 } 60 } 61 } 62 return 0; 63 }