题意
第一行输入测试组数,每组测试例的第一行输入两个整数n,m表示 昆虫数 和 交配的组数 ,后面m行每一行表示 i(昆虫的编号) 和 j 交配。判断是否有同性相交, 是输出 Suspicious bugs found!,否则输出 No suspicious bugs found! 。
一开始以为奇数代表一类,偶数代表一类,结果。。。太天真了
思路
1、根据挑战程序设计里边 食物链 的做法,将 pre 数组设为原来的两倍,1~n表示一类,n+1到 2n 表示另一类。每次输入只需判断父节点是否相同即可。不相同则合并,相同则为同性交配。食物链
2、并查集的常用解法。用数组 rank 记录当前点与其父节点的关系,同性为0,异性为1。每输入一组数据判断其父节点是否相同,若不相同则合并,否则判断是否为同性。如果rank[i] 和 rank[ pre[i] ] 的值相同( 此时rank[ pre[i] ] 实际上表示的是 i 的 父亲节点与 i 的祖先节点之间的关系)都是1 或者都是 0,可以推测出此时若将 i 链接到其祖先节点的话,rank[i] 应该变为0。若值不相同,则应变为1。(公式不理解的可以自己动手写一写,比较容易推出)
法一、
#include<cstdio>
const int maxn=2505;
int pre[maxn*2]; // 1道n为一类(同一性别) n+1到2n为另一类(同一性别)
int find(int x)
{
return pre[x]==x?x:pre[x]=find(pre[x]);
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x!=y)
pre[x]=y;
}
int main()
{
int t,n,m;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
int k=0;
scanf("%d%d",&n,&m);
for(int j=0;j<=n*2;j++)
pre[j]=j;
int a,b;
for(int j=0;j<m;j++)
{
scanf("%d%d",&a,&b);
if(find(a)!=find(b)) // 不为同性
{
//因为不知道a b的性别 所以两种情况同时成立
merge(a,b+n);
merge(a+n,b);
}
else //同性
k=1;
}
printf("Scenario #%d:\n",i);
if(k==1)
printf("Suspicious bugs found!\n\n");
else
printf("No suspicious bugs found!\n\n");
}
return 0;
}
法二‘
#include<cstdio>
const int maxn=2505;
int pre[maxn]; //父节点
int rank[maxn]; // 与父节点的关系
void init(int k)
{
for(int i=0;i<=k;i++)
{
pre[i]=i;
rank[i]=0;
}
}
int find(int x)
{
if(pre[x]==x)
return x;
int t=find(pre[x]);
rank[x]=(rank[x]+rank[pre[x]])%2; //利用函数的递归来算当前节点与其父节点的关系 从根节点往当前点算
pre[x]=t;
return t;
}
void merge(int x,int y)
{
int fx=find(x),fy=find(y);
pre[fx]=fy;
rank[fx]=(rank[x]+rank[y]+1)%2; // 合并时因为两节点一定是异性(有判断条件可知),所以需要加 1
}
int main()
{
int t,n,m;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
int f=0;
scanf("%d%d",&n,&m);
init(n);
for(int j=0;j<m;j++)
{
int a,b;
scanf("%d%d",&a,&b);
if(find(a)!=find(b))
merge(a,b);
else
{
if(rank[a]==rank[b]) // 父节点相同 且与父节点关系相同则为同性
f=1;
}
}
printf("Scenario #%d:\n",i);
if(f==1)
printf("Suspicious bugs found!\n\n");
else
printf("No suspicious bugs found!\n\n");
}
return 0;
}
这是我的一点见解,欢迎各位指教