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;
}