A Bug's Life
BackgroundProfessor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.
Input
The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.Output
The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.Sample Input
2 3 3 1 2 2 3 1 3 4 2 1 2 3 4
Sample Output
Scenario #1: Suspicious bugs found! Scenario #2: No suspicious bugs found!
题意:给出n条昆虫,m对关系。给出a,b表示a与b属于异性。问是否出现了同性恋昆虫。
解析:
解法一:跟之前的POJ1703一个解法,对称一下,a,b为异性,那么a+n,b为同性 a,b+n为同性。每次判断一下即可。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int maxn=1e4; bool vis[maxn]; int pr[maxn]; int n,m; int find(int x) { if(x!=pr[x]) pr[x]=find(pr[x]); return pr[x]; } void join(int x,int y) { int f1=find(x); int f2=find(y); if(f1!=f2) { pr[f1]=f2; } return ; } void init() { for(int i=1;i<=2*n+100;i++) pr[i]=i; } bool check(int x,int y) { if(find(x)!=find(y)) return false; return true; } int main() { int t; scanf("%d",&t); int ac=1; while(t--) { scanf("%d%d",&n,&m); int ok=0; init(); while(m--) { int x,y; scanf("%d%d",&x,&y); join(x+n,y); join(x,y+n); if(check(x,y)||check(x+n,y+n)) { ok=1; } } // for(int i=1;i<=2*n;i++) // cout<<pr[i]<<" "; // cout<<endl; printf("Scenario #%d:\n",ac++); if(ok) cout<<"Suspicious bugs found!"<<endl; else cout<<"No suspicious bugs found!"<<endl; cout<<endl; } }
解法二:用带权并查集的方式做。加入vis[]来描述性别异同,这里统一为0,1两个数字。vis[i]==0表示i与它的根节点为同性,==1为异性。即vis[]描述的是当前昆虫与它的根节点的性别异同关系。find()和join()函数就必须做出改变了,find里面必须要有路径压缩。即:
先说find(),找C节点的父亲节点,pr[C]=B,会先找到B,而vis[C]记录的是C与B的异同关系,所以到最终的父亲节点就不是这个vis[C]了,所以我们要更新vis[C]。即为vis[C]=(vis[C]+vis[pr[C]])%2了。这个可以自己设个样例找一下,的确是这个公式将vis[C]指向了A节点。随着父亲节点不停的改变,中途的vis[]也是随着变动。
if(x!=pr[x]) { int father=find(pr[x]); vis[x]=(vis[x]+vis[pr[x]])%2; pr[x]=father; } return pr[x];
再说join(),1:进入的a,b如果根节点相同,那么直接判断它们的vis[]是否相同,相同即为同性恋,否则不是。2:a,b的根节点不同,那么就将a,b所属的两棵树连接起来。这个时候是默认a,b的确如输入所表达的意思:异性。那么a,b的根节点的链接,需要建立起联系,需要由vis[a]和vis[b]来建立。公式就是:vis[father(a)]=(vis[a]+vis[b]+1)%2; 这样father(a)和father(b)的性别异同关系就建立起来了。
总的来讲,这个解法,每次是先判断,再执行操作的,发现同性恋,就不进行操作了。否则就默认正常,再操作下去。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=2e3+10; int pr[maxn],vis[maxn]; int n,m; void init() { for(int i=1;i<=n;i++) { pr[i]=i; vis[i]=0; } } int find(int x) { if(x!=pr[x]) { int father=find(pr[x]); vis[x]=(vis[x]+vis[pr[x]])%2; pr[x]=father; } return pr[x]; } int join(int x,int y) { int f1=find(x),f2=find(y); if(f1==f2) { if((vis[x]+vis[y])%2==0) return 1; else return 0; } else { pr[f1]=f2; vis[f1]=(vis[x]+vis[y]+1)%2; } return 0; } int main() { int t; scanf("%d",&t); int ac=1; while(t--) { scanf("%d%d",&n,&m); init(); int ok=0; while(m--) { int a,b; scanf("%d%d",&a,&b); if(join(a,b)==1) { ok=1; } } printf("Scenario #%d:\n",ac++); if(ok) { cout<<"Suspicious bugs found!"<<endl; } else { cout<<"No suspicious bugs found!"<<endl; } cout<<endl; } }