题目大意:博士认为只有性别不同的虫子才能交配,给出n只虫子的m次交配情况,判断博士的论断是否正确。
输入:case个数
第i个case的n m
第j次交配的虫子序号(空格分隔两只虫子)
输出:Scenario #i:
如果不矛盾则输出No suspicious bugs found!
如果矛盾则输出Suspicious bugs found!
分析:并查集的应用。deep[]数组记录与父节点的性别关系,同性为0,异性为1。
当输入一次AB交配时,AB的父节点xy(所属并查集的根)有两种情况:
1.x和y在同一个并查集中,这意味着在之前输入的关系中,AB分别已经与根有关系了,所以直接判断他们相对于根(同一个根)的关系,如果关系相同则意味着AB同性,输出suspicious,不同就继续输入下一次交配
2.x和y不在同一个并查集中,他们之间没有关系也就是说不可能出现错误的情况,现在只需要将他们进行正确的合并即可。合并方法:将标号大的(B)合并到标号小的(A)并查集中,需要修改B的父节点y相对于A的父节点x的性别关系。
假设A相对x的性别关系deep[A],那么B相对于x的性别关系就是deep[B]=(deep[A]+1)%2
假设B相对于y的性别关系是deep[B],那么反过来y相对于B的关系也是deep[B]
那么y相对于x的关系deep[y]就是((deep[A]+1)%2+deep[B])%2,,也就是说y相对于B的关系再加上B相对于x的关系就是y相对于x的关系。
代码:转载自http://blog.csdn.net/i_want_to_be_a_god/article/details/38379281
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define Max 2010
- int deep[Max]; //与父节点的性别关系,0为与父节点同性,1为与父节点异性
- int set[Max]; //并查集(父节点)
- int Case,n,m;
- int find(int x){ // 查
- if(set[x]==x) //若循环到父节点,返回父节点标号
- return x;
- int temp=find(set[x]); //找到x的所在并查集的总根节点
- deep[x]=(deep[set[x]]+deep[x])%2; //更新x相对于总根的性别关系,即为相对于原根的关系+原根相对于总根的关系。
- set[x]=temp; //路径压缩,将x连接到他所在并查集的总根上
- return temp; //返回父节点
- }
- int main(){
- scanf("%d",&Case);
- for(int i=1;i<=Case;i++){
- scanf("%d%d",&n,&m);
- memset(deep,0,sizeof(deep)); //初始化为全0
- for(int j=1;j<=n;j++) // 初始化为其自身
- set[j]=j;
- bool trag=true; //设置标记为没有同性恋
- int a,b;
- while(m--){
- if(trag){
- scanf("%d%d",&a,&b);
- int x=find(a);
- int y=find(b);
- if(x==y){ //在同一关系集合时
- if(deep[a]==deep[b]) //相对于父节点关系相同则意味着性别相同
- trag=false; //置标记为false
- }
- else{ //不再同一关系集合中,则一定正确,但要正确和并
- if(x<y){ // 将标号大的并向标号小的
- deep[y]=((deep[a]+1)%2+deep[b])%2; //改变标号大的父节点相对于标号小的父节点的关系
- set[y]=x; //大并小
- }
- else{ //同上
- deep[x]=((deep[b]+1)%2+deep[a])%2;
- set[x]=y;
- }
- }
- }
- else //若已经判定是同性恋关系,则不予判断,直接输入即可
- scanf("%d%d",&a,&b);
- }
- if(trag) //输出,注意首字母大小写
- printf("Scenario #%d:\nNo suspicious bugs found!\n\n",i);
- else
- printf("Scenario #%d:\nSuspicious bugs found!\n\n",i);
- }
- return 0;
- }