已知有 n 个学生,其中有男生也有女生,给定 m 个关系,每个关系表示两个同学性别是否相同。如 同学A 和同学 B 是同性,或不是同性,判断这些条件中是否存在矛盾。
Input
第一行为两个整数 n(1 <= n <= 1000), m(1 <= m <= 1000),每个同学编号为1到n,之后m行,每行三个整数,前两个整数表示两个同学的编号,第三个整数为0或者1,若为0,则表示两个同学为同性,否则为异性。
Output
一行,若存在矛盾,输出YES,否则输出NO
Sample Input
5 5 1 2 1 2 3 1 3 4 1 4 5 1 5 1 0
Sample Output
NO
这个和食物链是一个思想。借鉴的挑战程序设计。不过食物链的代码在poj上老是过不了,所以。。。。
代码里有注释
#include<cstdio>
#include<cstring>
using namespace std;
int f[3000];//大小是题目要求的三倍。
int fin(int n)//正常的并查集
{
if(n==f[n])
return n;
else {
return f[n]=fin(f[n]);
}
}
void init(int n)
{
for(int i=0;i<n;i++)
f[i]=i;
}
int merg(int v,int u)
{
int t1,t2;
t1=fin(v);
t2=fin(u);
if(t1!=t2)
{
f[t1]=t2;
}
}
bool same(int x,int y)//来判断两个节点的根节点是不是同一个
{
return fin(x)==fin(y);
}
int x[1005],y[1005],t[1005];
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(t,0,sizeof(t));
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&x[i],&y[i],&t[i]);
}
int ans=0;
init(2*n);//注意是两倍的n,用0-n表示一个性别,然后用n+1--2n表示另一个性别。
for(int i=0;i<m;i++)
{
int a,b,c;
a=x[i]-1;b=y[i]-1;c=t[i];
if(a<0||a>=n||b<0||b>=n)//判断超没超范围
{
ans++;
break;
}
if(c==1)
{
if(same(a,b)||same(a+n,b+n))//关键点。因为c=1,他们不相同,然后判断a和b a+n和b+n的根节点是不是一个
{
ans++;
break;//是 就矛盾了
}
else {
merg(a,b+n);// 把不矛盾的情况并到一起
merg(a+n,b);
}
}
else {
if(same(a,b+n)||same(a+n,b))//同理 判断是否矛盾
{
ans++;
break;
}
else {
merg(a,b);
merg(a+n,b+n);
}
}
}
if(ans==0)
printf("NO\n");
else printf("YES\n");
}
}