POJ1703(Find them, Catch them)

题目传送门
在这里插入图片描述

题意

这题大概意思就是有两个黑帮帮派,然后给定两个操作。
A操作:询问a,b的关系,如果a,b是同一个帮派的输出"In the same gang.",如果a,b不是同一个帮派的输出"In different gangs.",如果不确定则输出"Not sure yet."。
D操作:a,b两个人属于两个帮派。

其实这道题应该这样说更好理解,D操作代表两个人是异性,A操作就是询问两个人是否能是同性,还是异性,或者是不确定。题意很简单易懂,但是做起来里面还是小有名堂的。这道题是并查集的拓展-----叫种类并查集

思路

种类并查集:假设1表示两个人是异性,0表示两个人是同性,然后就是并查集怎么去维护这么多人之间的关系呢。假设现在开两个数组:

  1. s[i]代表i号节点的父节点。
  2. r[i]代表i号节点和父节点之间的关系。如果r[i] = 1,则表示i号节点和父节点s[i]是异性关系,r[i] = 0表示i号节点和父节点s[i]的关系是同性关系。
  3. 现在我们并查集就需要维护两个东西,一个是数组s和一个数组r。
  4. 父节点并不知道它和子节点的关系,只有子节点知道它和父节点的关系
  5. 重要前提是给定的a 和 b可以确定他们不是同性

情况分析
** 最开始初始化的时候每个人对自己都是同性,也就是r数组全是0!**
6. 给定a是雄性,b和c是雌性,然后就可以得到下面的图。最开始c和a是异性,所以r[c] = 1,a和b又是异性r[b] = 1,那么c和b就是同性。如果我们做路径压缩把c → b上那么r[c] = 0,因为c 和 b同性。
在这里插入图片描述

  1. 给定a和c是雄性,b是雌性也可以得到下面的图,同样的道理可以得到压缩后的关系。

在这里插入图片描述
仔细一点可以发现节点 C 路径压缩前后:
3. 压缩前r[c]表示的是节点c 和节点a 的关系。
4. 压缩后r[c]表示的是节点c 和节点b 的关系,实际上就是当前节点和根节点的值。
仔细观察可以得出结论1:r[c] = (r[c] + r[a]) = (r[c] + r[s[c]]) % 2;
在这里插入图片描述

在这里插入图片描述
合并c 到 e上的时候怎么确定根节点a 和 根节点d的关系
**结论2:r[a] = (r[c] + r[e] + 1)%2;**确定完了这两个重要结论就可以写代码了。
最后判断的时候也是挺简单的,重要的是里面的结论和思路。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
int s[maxn];			//父节点 
int r[maxn];			//当前节点和父节点的关系
void clear_set()
{
	for(int i = 0;i < maxn;i++){
		s[i] = i;
	}
	memset(r,0,sizeof(r)); 				//最开始自己和自己同性
}
int find_set(int x)
{
	if(x != s[x]){
		int f = s[x];
		s[x] = find_set(s[x]);
		r[x] = (r[x] + r[f])%2;				//路径压缩应该对r[x]的值进行更新
		return s[x]; 
	}
	return x;
}
void union_set(int x,int y)
{
	int fx = find_set(x);
	int fy = find_set(y);
	if(fx != fy){
		s[fx] = fy;
		r[fx] = (r[x] + r[y] + 1)%2;
	}
}
int main()
{
	int t,n,m;
	scanf("%d",&t);
	while(t--){
		clear_set();
		scanf("%d%d",&n,&m);
		while(m--){
			char str[5];
			int x,y;
			scanf("%s%d%d",str,&x,&y);
			if(str[0] == 'D'){
				union_set(x,y);
			}
			else{
				int fx = find_set(x);
				int fy = find_set(y);
				if(fx == fy){
					if((r[x] + r[y])%2 == 0){
						printf("In the same gang.\n");
					}
					else{
						printf("In different gangs.\n");
					}
				}
				else{
					printf("Not sure yet.\n");
				}
			}
		} 
	}
	return 0;
}

愿你走出半生,归来仍是少年~

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值