假设有
1,2
2,3
即1--2--3, 明显相邻的两个不能是同性别的,如果相邻两个是同性的,那么说明就可能是有同性恋存在。
上面假设1是男性,用false来代替,那么按顺序分别是false, true, false
假设 再加个关系3, 1
那么由于1和3已经都有值了,都是false,说明可能有同性恋。
种类并查集的关键在于与结点与根结点的距离, 如果距离是奇数那么性别就和跟结点相反,如果是偶数就和跟结点性别相同。
写法一:
#include<cstdio>
#define N 2005
using namespace std;
int f[N],rank[N], n, k;
bool flag;
inline void init(){
flag=false;
for(int i=0; i<=n; ++i)
f[i]=i, rank[i]=0;
}
int find(int x){
if(x==f[x])return f[x];
int t=find(f[x]);
rank[x] = (rank[f[x]]+rank[x])&1;
f[x]=t;
return f[x];
}
void Union(int x, int y){ //执行了这个方法 表示x和y要么之前没关系,现在要加上异性关系,如果之前有关系,要判断是什么关系
int a=find(x), b=find(y); //并查集的合并时合并两个父节点!!!
if(a==b){
if(rank[x]==rank[y])
flag=true;
return;
}
f[a]=b; //如果之前x和y没关系,现在要合并,因为是异性,所以高度要相差一位,
rank[a] = (rank[x]+rank[y]+1)&1; //知道的信息只有x和y是异性
}
int main(){
int T,a,b,cas=1;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
init();
for(int i=0; i<k; ++i){
scanf("%d%d",&a,&b);
if(flag)continue;
Union(a,b);
}
printf("Scenario #%d:\n",cas++);
if(flag)printf("Suspicious bugs found!\n");
else printf("No suspicious bugs found!\n");
printf("\n");
}
return 0;
}
写法二:类似食物链的那道题写法
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int father[5005];
int found(int x){
if(x!=father[x])
father[x]=found(father[x]);
return father[x];
}
void unite(int x,int y){
x=found(x);
y=found(y);
if(x==y)
return;
father[x]=y;
}
bool same(int x,int y){
return found(x)==found(y);
} //并查集模板
int main(){
int i,j,p,t,n,k,x,y,ans;
scanf("%d",&t);
for(p=1;p<=t;p++){
ans=0;x
scanf("%d%d",&n,&k);
for(i=1;i<=2*n;i++)
father[i]=i;
while(k--){
scanf("%d%d",&x,&y);
if(same(x,y)||same(x+n,y+n)) //x,y同性时不符合情况
ans++;
else{
unite(x,y+n);
unite(x+n,y);
}
}
printf("Scenario #%d:\n",p);
if(!ans)
printf("No suspicious bugs found!\n\n");
else
printf("Suspicious bugs found!\n\n");
}
return 0;
}