码迷,mamicode.com
首页 > 其他好文 > 详细

POJ - 3678 Katu Puzzle (2-SAT)

时间:2019-08-25 00:42:35      阅读:105      评论:0      收藏:0      [点我收藏+]

标签:name   algo   time   图片   bre   splay   tac   考点   ring   

(点击此处查看原题)

题意

有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)

标签:name   algo   time   图片   bre   splay   tac   考点   ring   

原文地址:https://www.cnblogs.com/winter-bamboo/p/11406573.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!