题目地址:POJ1703【Find them, Catch them】
题目描述:
Tadu市的警察局决定结束混乱,因此要采取行动,根除城市中的两大帮派:龙帮和蛇帮。然而,警方首先需要确定某个罪犯是属于哪个帮派。目前的问题是,给出两个罪犯,他们是属于同一个帮派吗?您要基于不完全的信息给出您的判断,因为歹徒总是在暗中行事。假设在Tadu市现在有N(N≤105)个罪犯,编号从1到N。当然,至少有一个罪犯属于龙帮,也至少有一个罪犯属于蛇帮。给出M(M≤105)条消息组成的序列,消息有下列两种形式:
- D [a] [b]
其中[a]和[b]是两个犯罪分子的编号,他们属于不同的帮派; - A [a] [b]
其中[a]和[b]是两个犯罪分子的编号,您要确定a和b是否属于同一帮派。
输入描述:
输入的第一行给出给出一个整数T(1≤T≤20),表示测试用例的个数。后面跟着T个测试用例,每个测试用例的第一行给出两个整数N和M,后面的M行每行给出一条如上面所描述的消息。
输出描述:
对于在测试用例中的每条“A [a] [b]”消息,您的程序要基于此前给出的信息做出判断。回答是如下之一 “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.
思路:
龙帮和蛇帮的罪犯各组成一个集合,设set[d]为罪犯d所属集合的代表元,set[d+n]为另一集合的代表元,1≤d≤n。函数set_find(i)查找罪犯i所属并查集的代表元,同时进行路径压缩,1≤i≤2n。
初始时set[d]=-1,即每个罪犯自成一个帮派。按照如下方法处理每条消息s:
确定a和b是否属于同一帮派(s[0]==‘A’)
- 如果a和b不属同一帮派(set_find(a)!=set_find(b)),且a所属的帮派与b的另一帮派也不相同(set_find(a)!=set_find(b+n)),则不能确定a和b是否属于同一帮派;
- 否则,如果罪犯a所属集合的代表元与罪犯b所属集合的代表元相同(set_find(a)=set_find(b)),则确定a和b同属一个帮派;
- 否则,可以确定a和b属于不同的帮派。
题记:
find_set函数中返回的是set[p] = find_set(set[p]),而不是find_set(set[p])
参考链接:并查集讲解
代码:
#include<cstdio>
#include<cstring>
const int maxn = 1e5+5;
int set[maxn + maxn];
int find_set(int p)// 查找p所在集合的代表元,用路径压缩优化
{
if(set[p] < 0) return p;
return set[p] = find_set(set[p]);
}
void join_set(int p,int q)// 将p所在的集合并入q所在的集合
{
p = find_set(p);
q = find_set(q);
if(p != q)
set[p] = q;
}
int main()
{
int t;
char str[5];
scanf("%d",&t);
while(t--)
{
int n,m,a,b;
memset(set,-1,sizeof(set));
scanf("%d %d",&n,&m);
while(m--)
{
scanf("%s%d%d",str,&a,&b);
if(str[0] == 'A')
{
if(find_set(a) != find_set(b))//如果a 不属于 b 组织
if(find_set(a) != find_set(b+n))//如果a 不属于 b 的另一组织
printf("Not sure yet.\n");
else// a 属于 b 的另一组织
printf("In different gangs.\n");
else//a b 属于同一个组织
printf("In the same gang.\n");
}
else
{
join_set(a,b+n);//a 属于 b 的另一组织组织
join_set(b,a+n);//b 属于 a 的另一组织组织
}
}
}
return 0;
}