hdu 3062 Party【2-SAT入门---强连通Tarjan+染色缩点】

来源:互联网 发布:美工自学首先学什么 编辑:程序博客网 时间:2024/05/21 09:48

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





0 0