POJ 3678 2-SAT

因为A和B的取值只有0,1,在运算里就可以用2-SAT来解决。

建图过程如下:

1) X AND Y=1,Add(I',J'),Add(J',I'),Add(I,I'),Add(J,J')

2) X AND Y=0,Add(I',J),Add(J',I)

3) X OR Y=1,Add(I,J'),Add(J,I')

4) X OR Y=0,Add(I,J),Add(J,I),Add(I',I),Add(J',J)

5) X XOR Y=1,Add(I',J),Add(J',I),Add(I,J'),Add(J,I')

6) X XOR Y=0,Add(I',J'),Add(J',I'),Add(I,J),Add(J,I)


一开始我没理解红色部分的建图。后来我才知道,如果没有红色部分,那么有一部分的冲突就没有解决。比如:样例里同时有A OR B = 0, A OR B = 1。如果没有下面两句话,Add(I',I),Add(J',J),那么程序是判断不出冲突的,因为在建图的时候就只有A<->B,A -> B', B -> A'。A和A’,B和B‘并不在一个强连通分量里,所以无法判别冲突,画个图看一下就知道了。

对于红色部分更好的解释应该是:X OR Y的时候,X或Y任何一个不能为1,否则就会有冲突,因此从X'和Y'向X, Y连接一条边。


Code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 2222;
const int maxm = 2222222;
struct node
{
    int v, next;
}edge[maxm];

int head[maxn], k, n, m;
int low[maxn], dfn[maxn], belong[maxn], inde, cnt;
int stack[maxn << 2], top;
bool instack[maxn];

void add_edge(int u, int v)
{
    edge[k].v = v, edge[k].next = head[u];
    head[u] = k ++;
}

void tarjan(int u)
{
    int v;
    dfn[u] = low[u] = inde ++;
    instack[u] = true;
    stack[top ++] = u;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(instack[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        cnt ++;
        do{
            v = stack[-- top];
            instack[v] = false;
            belong[v] = cnt;
            num[cnt] ++;
        }while(v != u);
    }
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        memset(head, -1, sizeof(head));
        k = 0;
        while(m --)
        {
            int u, v, val;
            char op[10];
            scanf("%d%d%d%s", &u, &v, &val, op);
            if(op[0] == 'A')
            {
                if(val == 0)
                {
                    add_edge(u + n, v);
                    add_edge(v + n, u);
                }
                else
                {
                    add_edge(u + n, v + n);
                    add_edge(v + n, u + n);
                    add_edge(u, u + n);  //   u can't be 0
                    add_edge(v, v + n);  //   v can't be 0
                }
            }
            else if(op[0] == 'O')
            {
                if(val == 0)
                {
                    add_edge(u, v);
                    add_edge(v, u);
                    add_edge(u + n, u);  //   u can't be 1
                    add_edge(v + n, v);  //   v can't be 1
                }
                else
                {
                    add_edge(u, v + n);
                    add_edge(v, u + n);
                }
            }
            else if(op[0] == 'X')
            {
                if(val == 0)
                {
                    add_edge(u, v);
                    add_edge(v, u);
                    add_edge(u + n, v + n);
                    add_edge(v + n, u + n);
                }
                else
                {
                    add_edge(u, v + n);
                    add_edge(v, u + n);
                    add_edge(v + n, u);
                    add_edge(u + n, v);
                }
            }
        }
        memset(dfn, 0, sizeof(dfn));
        memset(instack, false, sizeof(instack));
        top = 0, cnt = 0, inde = 1;
        int nn = 2 * n;
        for(int i = 0; i < nn; i ++)
            if(!dfn[i])
                tarjan(i);
        bool ok = true;
        for(int i = 0; i < nn; i ++)
            if(belong[i] == belong[i + n])
                ok = false;
        if(!ok)
            printf("NO\n");
        else
            printf("YES\n");
       
    }
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值