Hdu 3062. Party

本文介绍了一个典型的2-SAT问题实例,通过Tarjan算法寻找强连通分量来判断是否有解。该问题涉及n对夫妻参加聚会,但因矛盾关系限制,每对夫妻只能有一人出席。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description

有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?

Input

n: 表示有n对夫妻被邀请 (n<= 1000)
m: 表示有m 对矛盾关系 ( m < (n - 1) * (n -1))

在接下来的m行中,每行会有4个数字,分别是 A1,A2,C1,C2
A1,A2分别表示是夫妻的编号
C1,C2 表示是妻子还是丈夫 ,0表示妻子 ,1是丈夫
夫妻编号从 0 到 n -1

Output

如果存在一种情况 则输出YES
否则输出 NO

Sample Input

2
1
0 1 1 1

Sample Output

YES

Solution

  • 经典的 2-SAT 做法。

  • 拆点,将选了一个就必须选另一个的这样一组,连上一条有向边。

  • 之后跑一遍 Tarjan 求强联通分量,若矛盾的两点在同一强连通分量中就无解。

Code

#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
const int N=2005;
int n,tot,top,num;
int first[N],nex[N*N<<1],en[N*N<<1];
int dfn[N],low[N],stack[N],f[N];
bool bz[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void insert(int x,int y)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void tarjan(int x)
{
    dfn[x]=low[x]=++tot;
    bz[stack[++top]=x]=true;
    for(int i=first[x];i;i=nex[i])
        if(!dfn[en[i]])
        {
            tarjan(en[i]);
            low[x]=min(low[x],low[en[i]]);
        }else
            if(bz[en[i]]) low[x]=min(low[x],dfn[en[i]]);
    if(dfn[x]==low[x])
    {
        num++;
        do
        {
            f[stack[top]]=num;
            bz[stack[top--]]=false;
        }while(stack[top+1]^x);
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        int m=read();
        tot=num=0;
        memset(first,0,sizeof(first));
        memset(dfn,0,sizeof(dfn));
        while(m--)
        {
            int x=read(),y=read();
            x=x<<1|read(),y=y<<1|read();
            insert(x,y^1);
            insert(y,x^1);
        }
        n<<=1,tot=0;
        for(int i=0;i<n;i++)
            if(!dfn[i]) tarjan(i);
        bool pd=true;
        for(int i=0;i<n;i+=2)
            if(f[i]==f[i^1])
            {
                pd=false;
                break;
            }
        puts(pd?"YES":"NO");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值