POJ 3678 Katu Puzzle 2-SAT

题意:

n个权值为0或1的数。
给定c个限制条件,形如:
XA xor XB=1(0)
XA or XB=1(0)
XA and XB=1(0)
判定是否可以安排这n个数的值,满足所有限制条件。

解析:

据说是2-SAT问题,反正我刚看到的时候满脑子都是建出来一个图之后判环。。
然而是有科学的做法的。
首先我们把每个点拆分成两个,分别代表改点取0或1.
科学的做法就是按照矛盾关系建边。
打一个比方
如果 XA xor XB=1 的话,
XA 取1的时候, XB 一定取0,所以连一条边。
XA 取0的时候, XB 一定取1,所以连一条边。
XB 取1的时候, XA 一定取0,所以连一条边。
XB 取0的时候, XA 一定取1,所以连一条边。
每一种限制条件都像这样讨论就好了。
但是需要注意的是,有的时候 XA 取0(1)一定无解。
所以我们要相应的从该点连向 XA 取1(0)的边,代表如果 XA 取0(1)一定无解。
最后我们只需要tarjan缩下点观察每个点取0或者1的两个点是否在一个强连通分量里即可。
如果有一个在的话,那么显然无解。
否则有解。

如果这道题要输出方案的话,显然有解的情况最后的图是一个DAG,拓扑染色一下即可。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2100
using namespace std;
int head[N];
int cnt,n,m;
struct node
{
    int from,to,next;
}edge[N*N];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void edgeadd(int from,int to)
{
    edge[cnt].from=from,edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
char s[5];
int cnt_block,tot,top;
int deep[N],low[N],sta[N],belong[N],ins[N];
void tarjan(int now)
{
    deep[now]=low[now]=++tot;
    sta[++top]=now,ins[now]=1;
    for(int i=head[now];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(!deep[to])
        {
            tarjan(to);
            low[now]=min(low[now],low[to]);
        }else if(ins[to])low[now]=min(low[now],deep[to]);
    }
    if(deep[now]==low[now])
    {
        cnt_block++;
        int t=-1;
        do
        {
            t=sta[top--];
            belong[t]=cnt_block;
            ins[t]=0;
        }while(t!=now);
    }
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y,val;
        scanf("%d%d%d%s",&x,&y,&val,s);
        x++,y++;
        if(s[0]=='X')
        {
            if(val==1)
                edgeadd(x+n,y),edgeadd(y,x+n),edgeadd(x,y+n),edgeadd(y+n,x);
            else 
                edgeadd(x+n,y+n),edgeadd(y+n,x+n),edgeadd(x,y),edgeadd(y,x);
        }else if(s[0]=='A')
        {
            if(val==1)
                edgeadd(x,y),edgeadd(y,x),edgeadd(x+n,x),edgeadd(y+n,y);
            else edgeadd(x,y+n),edgeadd(y,x+n);
        }else
        {
            if(val==1)
                edgeadd(x+n,y),edgeadd(y+n,x);
            else edgeadd(x,x+n),edgeadd(y,y+n),edgeadd(x+n,y+n),edgeadd(y+n,x+n);
        }
    }
    for(int i=1;i<=2*n;i++)
        if(!deep[i])tarjan(i);
    int flag=1;
    for(int i=1;i<=n;i++)
        if(belong[i]==belong[i+n]){flag=0;break;}
    if(flag)puts("YES");
    else puts("NO");
}

你可能感兴趣的:(C语言,poj,X)