Party
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5548 Accepted Submission(s): 1806
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
Source
2009 Multi-University Training Contest 16 - Host by NIT
对于2-SAT:简单的说,就是将一个问题转化到这样一种问题:
一共有2n个元素,分成n个帮派,每个帮派两个人,每个帮派最多只能有一个人缺席的情况下,每个帮派之间还有相对矛盾的条件,要同时满足这种条件的前提下,问是否能够同时拿出n个元素的问题。
其实一共拿出n个元素,也就注定着同一个帮派的两个人不能同时上场。那么就是处理相对矛盾的关系。
设定(a0,a1)为一个帮派,设定(b0,b1)是另一个帮派。
如果有a0-----b0这样的矛盾关系,那么在选取的时候,如果在帮派1中选a0,那么就必须在帮派2中选b1,那么我们建有向边(a0,b1),如果在帮派2中选b0,那么就必须在帮派1中选a1,那么我们建有向边(b0,a1)。
这个时候我们得到一个图,我们仔细考虑一下,图中的有向边的含义:对于图中的一条有向边,表示要选择一方,就必然选择另一方。然后我们来观察比对三个图,探讨一下什么时候输出YES,什么时候输出NO。
1、
①对于这个图来说,显然输出的结果是YES,其中约束条件只有这两个:如果选了A0,那就要选B1,如果选B0,那么就要选A1。
②对于这个图来说,其中满足YES条件的选择有好几个选择,比如:(A0,B1),(A1,B1).....
2、
①对于这个图来说,显然输出的也是YES。
②对于这个图来说,仔细分析一下,发现确实有不满足的情况,比如:如果我们选择了A0,那么就必须选择B1和B0,那么相对的就要必须选择A1,我们在最初的时候就分析过这样一种情况:同一帮派两个人如果同时选取了,那么显然不能满足符合正好选n个人的条件。但是仔细分析一下还是可以找到满足的条件的选择方案啊的,满足条件的选择比如这样:(B0,A1)就是一个满足的条件。
接下来我们观察第三个图:
3、
对于这种情况,显然没有任何选取的方式,能够使得A0,A1不同时选取。那么就输出NO。
分析完了三个图,其实我们得到了这样一个经验:如果在图中找选择的方案的时候,存在任意一种情况能够使得A0和A1不在同一个约束条件中就能够符合我们要求的条件,也就是,如果A0选取了,A1如果可以不选取,就满足,如果A0选取了,A1也必须选取,那么就不满足。
那么问题其实就可以转化到这样:
如果A0有一条路径能够到达A1,那么就表示如果选取A0,那么图中存在一种情况选择A0的同时必须选择A1,但是这里注意,只是存在一种情况,但是不代表别的路径情况在选A0的时候就必须选A1,这种情况详情见图2.
如果A0有一条路径能够到达A1,同时A1有一条路径能够到达A0,也就是说A0.A1是互达的,那么A0,A1一定在一个强连通分量中,那么如果有这样的情况存在,那么如果我们选取了强连通分量中的任意一个点,那么就一定能选到A0和A1.......那么就是说,这种情况一定是会有A0,A1同时选取的矛盾存在,那么也就是说:
如果A0,A1在同一个强连通分量中,那么恭喜你,你找到了输出NO的情况,如果没有这种情况存在,那么就是输出YES的情况。
算法实现相关:
Tarjan/Kosaraju算法找强连通分量,缩点染色,然后判断夫妻之间是否在同一个强连通分量中即可。
AC代码:
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
int head[100000];
struct EdgeNode
{
int to;
int w;
int next;
}e[1000000];
int vis[100000];
int low[100000];
int dfn[100000];
int color[100000];
int stack[1000000];
int n,m,cont,sig,cnt,tt;
void add(int from,int to)
{
e[cont].to=to;
e[cont].next=head[from];
head[from]=cont++;
}
void init()
{
cont=0;
tt=-1;
sig=0;
cnt=1;
memset(stack,0,sizeof(stack));
memset(dfn,0,sizeof(dfn));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(color,0,sizeof(color));
memset(head,-1,sizeof(head));
}
void Tarjan(int u)
{
vis[u]=1;
low[u]=dfn[u]=cnt++;
stack[++tt]=u;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(vis[v]==0)Tarjan(v);
if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
sig++;
do
{
vis[stack[tt]]=-1;
color[stack[tt]]=sig;
}
while(stack[tt--]!=u);
}
}
int Slove()
{
for(int i=0;i<n*2;i++)
{
if(vis[i]==0)
{
Tarjan(i);
}
}
for(int i=0;i<n*2;i+=2)
{
if(color[i]==color[i+1])return 1;
}
return 0;
/*
printf("%d\n",sig);
for(int i=0;i<n*2;i++)
{
printf("%d ",color[i]);
}
printf("\n");
*/
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=0;i<m;i++)
{
int a,b,x,y;
scanf("%d%d%d%d",&a,&b,&x,&y);
add(2*a+x,2*b+1-y);
add(2*b+y,2*a+1-x);
}
int tt=Slove();
if(tt==1)printf("NO\n");
else printf("YES\n");
}
}