种类判断

已知有 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");
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值