POJ 3678 - Katu Puzzle(2-SAT)

题目:

http://poj.org/problem?id=3678

题意:

有一个n元素的集合, 给出元素之间的关系,(and,or, xor),求出是否满足有满足这样关系的集合.

思路:

2-SAT, 每一个点只能取0/1.

规律:     a and b ==1 , !a->a , !b -> b (a=1, b=1)

            a and b ==0 , a->!b , b->!a  (a=0, b=0)

            a or b ==1 , !a->b , !b->a 

            a or b ==0 , a->!a , b->!b  (a=0, b=0)

            a xor b ==1 , a->!b,!b->a,!a->b,b->!a  ( xor 可以不加边的,因为存在如果a=1,那么b必=0;a=0,b必=1;b=1,a必=0;b=0,a必=1。所以出现a=1时就

            a xor b ==0 , a->b,b->a,!a->!b,!b->!a  不会出现a=0的情况,边之间不会矛盾,所以不存在belong[a]=belong[a+n]的情况.另一种说法是形成                           自环,a=0,b=1和b=1,a=0.)

AC.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>

using namespace std;
const int MAXN = 20005;
const int MAXM = 2e6+5;
int n, m;
struct edge {
    int to, next;
}edge[MAXM];
int head[MAXN], tot;

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int low[MAXN], dfn[MAXN], belong[MAXN];
int scc, Index;
stack<int> S;

void tarjan(int u)
{
    int v;
    low[u] = dfn[u] = ++Index;
    S.push(u);
    for(int i = head[u]; i != -1; i = edge[i].next) {
        v = edge[i].to;
        if(!dfn[v]) {
            tarjan(v);
            if(low[u] > low[v]) low[u] = low[v];
        }
        else if(!belong[v] && low[u] > dfn[v]) {
            low[u] = dfn[v];
        }
    }
    if(low[u] == dfn[u]) {
        scc++;
        do {
            v = S.top(); S.pop();
            belong[v] = scc;
        }while(v != u);
    }
}
bool solve()
{
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(belong, 0, sizeof(belong));

    Index = scc = 0;
    for(int i = 0; i < 2*n; ++i) {
        if(!dfn[i]) tarjan(i);
    }

    for(int i = 0; i < n; ++i) {
        if(belong[i] == belong[n+i]) return false;
    }
    return true;
}
int main()
{
//freopen("in", "r", stdin);
    while(~scanf("%d %d", &n, &m)) {
        init();
        int u, v, d;
        char f[5];
        
        for(int i = 0; i < m; ++i) {
            scanf("%d %d %d %s", &u, &v, &d, f);
            if(f[0] == 'A') {
                if(d == 1) {
                    addedge(n+u, u);
                    addedge(n+v, v);
                }
                else {
                    addedge(u, n+v);
                    addedge(v, n+u);
                }
            }
            if(f[0] == 'O') {
                if(d == 1) {
                    addedge(n+u, v);
                    addedge(n+v, u);
                }
                else {
                    //addedge(n+u, n+v);
                    addedge(u, n+u);
                    addedge(v, n+v);
                }
            }
//            if(f[0] == 'X') {
//                if(d == 1) {
//                    addedge(u, n+v);
//                    addedge(v, n+u);
//                    addedge(n+u, v);
//                    addedge(n+v, u);
//                }
//                else {
//                    addedge(n+u, n+v);
//                    addedge(v+n, n+u);
//                    addedge(u, v);
//                    addedge(v, u);
//                }
//            }
        }

        if(solve() ) {
            printf("YES\n");
        }
        else printf("NO\n");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值