POJ - 3678 Katu Puzzle (2-SAT)

Posted winter-bamboo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ - 3678 Katu Puzzle (2-SAT)相关的知识,希望对你有一定的参考价值。

(点击此处查看原题)

题意

有n个变量,编号为0~n-1,每个变量只会取0和1,此时有m对关系:a b c operator ,表示变量a,b满足 a operator b == c ,问对n个变量是否存在一种赋值,使得m对关系全部满足

解题思路

由题意就知道,这是一个2-SAT问题,给出了两个变量之间值的关系,相当于给出了推导关系,不过这个地方和常规的关系(如果a为真,那么b为假) 不一样,这里的关系是:a,b 满足 a operator b == c ,所以这个题的难度就在于如何根据形如 a operator b == c 这样的关系找到变量之间的推导关系 ,我们用a表示a取1,用¬a表示a为0

(加深一下2--SAT问题的处理方法:我们处理2-SAT问题的时候建的由a到¬b的边的关系为:当a为真,推出¬b也为真,也就是 a为真,¬b为真,b为假,所以对于任意变量x,如果存在x和¬x在同一强连通分量内,则无解,个人认为2-SAT问题的核心考点就是提取出变量之间的推导关系,其余的都是板子了)

下面给出每个关系对应的推导关系:

1)a AND b == 1 ,此时必须使得a,b同时为1,那么如果出现¬a或者¬b,那么必然是无解,所以建边 a →¬a 和 b  →¬b

2)a AND b == 0 ,此时我们有很多的可满足关系,但是推导关系只有:a为1,b必定为0 ; b为1,a必定为0,所以建边 a→¬b 和 b →¬a

3)a OR b == 1,有推导关系:a为0,则b为1;b为0.则a为1,所以建边 ¬a→b 和 ¬b→a 

4)a OR b == 0,此时必须使得a,b同时为0,那么如果出现a或者b,那么必然是无解,所以建边¬a →a 和 ¬b  →b

5)a XOR b == 1,此时a和b的值必定不同,所以建边: a→¬b 、 ¬a→b、b →¬a 和¬b→a 

6)a XOR  b == 0,此时a和b的值必定相同,所以建边:a→b 、 ¬a→¬b、b →a 和¬b→¬a 

根据以上关系建好图后,就是套用2-SAT的模板求解了

代码区

技术图片
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const ll inf = 2e9 + 10;
const ll mod = 1e9 + 7;
const int Max = 1e4 + 10;
const int Max2 = 3e2 + 10;

int n, m;
char order[5];
vector<int> edge[Max];
int dfn[Max], low[Max], time_clock;
int line[Max], now;
int id[Max], sccCnt;

void init()

    for (int i = 0; i < Max; i++)
        edge[i].clear();
    memset(id, 0, sizeof(id));
    now = sccCnt = 0;


void tarjan(int u)

    dfn[u] = low[u] = ++time_clock;
    line[++now] = u;        //记录访问次序
    for (int i = 0; i < (int) edge[u].size(); i++)
    
        int v = edge[u][i];
        if (!dfn[v])
        
            tarjan(v);
            low[u] = min(low[u], low[v]);
        
        else if (!id[v])        //不属于其他的连通分量,如果属于其他的强连通分量,那就破坏了此时不求强连通分量之间的关系的计划
        
            low[u] = min(low[u], dfn[v]);
        
    
    if (dfn[u] == low[u])        //此时代表从u向下走,又回到了u点,也就是成环了,我们将这一点定义为强连通分量的根结点
    
        sccCnt++;
        while (line[now] != u)    //同一个强连通分量中的点用同一个数标识
            id[line[now]] = sccCnt, now--;
        id[line[now]] = sccCnt, now--;
    



int main()

#ifdef LOCAL
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif
    while (scanf("%d%d", &n, &m) != EOF)
    
        init();
        for (int i = 1, a, b, val; i <= m; i++)
        
            scanf("%d%d%d%s", &a, &b, &val, order);
            if (order[0] == A)
            
                if (val == 1)
                    edge[a + n].push_back(a), edge[b + n].push_back(b);        //a,b不可能取0,如果出现,直接当作无解
                else
                    edge[a].push_back(b + n), edge[b].push_back(a + n);  //a为1,则b为0;b为1,则a为0
            
            else if (order[0] == O)
            
                if (val == 1)
                    edge[a + n].push_back(b), edge[b + n].push_back(a);         //a为0,则b为1;b为1,则a为1
                else
                    edge[a].push_back(a + n), edge[b].push_back(b + n);    //a,b不可能为1,如果出现,直接当作无解
            
            else
            
                if (val == 1)
                    edge[a].push_back(b + n), edge[a + n].push_back(b),
                    edge[b].push_back(a + n), edge[b + n].push_back(a);      //a,b总是不同
                else
                    edge[a].push_back(b), edge[a + n].push_back(b + n),
                    edge[b].push_back(a), edge[b + n].push_back(a + n);        //a,b总是相同
            
        
        for (int i = 0; i < (n << 1); i++)
            if (!dfn[i])
                tarjan(i);
        bool ok = true;
        for (int i = 0; i < n; i++)
        
            if (id[i] == id[i + n])
            
                ok = false;
                break;
            
        
        if (ok)
            printf("YES\n");
        else
            printf("NO\n");
    
    return 0;
View Code

以上是关于POJ - 3678 Katu Puzzle (2-SAT)的主要内容,如果未能解决你的问题,请参考以下文章

POJ 3678 Katu Puzzle

poj3678 Katu Puzzle

POJ3678 Katu Puzzle 2-sat

Poj3678:Katu Puzzle

[poj] 3678 Katu Puzzle

POJ 3678 Katu Puzzle