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