「学习笔记」2-SAT问题
SAT 是适定性 (Satisfiability) 问题的简称。一般形式为 k - 适定性问题,简称 k-SAT。而当 时该问题为 NP 完全的。所以我们只研究 的情况。
2-SAT,简单的说就是给出 个集合,每个集合有两个元素,已知若干个 ,表示 与 矛盾(其中 与 属于不同的集合)。然后从每个集合选择一个元素,判断能否一共选 个两两不矛盾的元素。显然可能有多种选择方案,一般题中只需要求出一种即可。
建图#
我们将 个集合拆成两个点,分别代表着 true
和 false
.
当 a || b == true
时,我们可以得知:
a == false
时,b = true
;
b == false
时,a = true
;
这两个关系存在因果关系;
而 a == true
时,我们不能得知 b = true
还是 b = false
,这两者之间不存在因果关系,b == true
同理。
因此,我们将 向 连边,将 向 连边。
含义:
当a == false
时,b = true
;
当b == false
时,a = true
。
当 a && b == false
时,我们可以得知:
a == true
时,b = false
;
b == true
时,a = false
;
我们将 向 连边,将 向 连边。
含义:
当a == true
时,b = false
;
当b == true
时,a = false
。
当 a && b == true
时,我们发现,a == true
,b == true
,除了这种情况不会再有其他情况了,即 的值一定为 true
, 的值一定为 true
。
这种情况下,我们将 向 连边, 向 连边。
含义:
当a == false
时,a = true
;(即 一定不为false
)
当b == false
时,b = true
。(即 一定不为true
)
判断是否有解#
如果 可以到达 ,说明 一定为 true
;
如果 可以到达 ,说明 一定为 false
。
判断是否有解即在一种情况中 都有唯一确定的值,要么为 true
,要么为 false
,倘若在同一种情况中, 可以到达 , 可以到达 ,则无法确定 的值,此情况下无解,即 与 在同一个强连通分量里。
用 tarjan 算法来找强连通分量即可。
题目#
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int n, m, tim, scc;
vector<int> son[N << 1], sta;
int dfn[N << 1], low[N << 1], lt[N << 1];
void tarjan(int u) {
dfn[u] = low[u] = ++ tim;
sta.push_back(u);
for (int v : son[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (!lt[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
lt[u] = ++ scc;
while (sta.back() != u) {
lt[sta.back()] = scc;
sta.pop_back();
}
sta.pop_back();
}
}
int main() {
scanf("%d%d", &n, &m);
while (m --) {
int xi, i, xj, j;
scanf("%d%d%d%d", &xi, &i, &xj, &j);
son[xi + n * (i & 1)].push_back(xj + (j ^ 1) * n);
son[xj + n * (j & 1)].push_back(xi + (i ^ 1) * n);
}
for (int i = 1; i <= (n << 1); ++ i) {
if (!dfn[i]) {
tarjan(i);
}
}
for (int i = 1; i <= n; ++ i) {
if (lt[i] == lt[i + n]) {
puts("IMPOSSIBLE");
return 0;
}
}
puts("POSSIBLE");
for (int i = 1; i <= n; ++ i) {
printf("%d%c", (lt[i] < lt[i + n]), " \n"[i == n]);
}
return 0;
}
CF1475F
将 和 两个矩阵异或,得到一个新矩阵 ,若 可以通过异或行或列的操作来变成全 矩阵,那么说明 可以通过异或得到 。
我们发现,每一行或每一列要么不异或,要么异或一次,由此可以想到 2-SAT。
对于 中的元素,若 为 ,则要么行异或,要么列异或;若 为 ,则要么行和列都异或,要么行和列都不异或,由此建边判断是否有解。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
int T, n, cnt, tim, scc;
int a[N][N], b[N][N], c[N][N], x[N][2], y[N][2];
int dfn[N << 2], low[N << 2], lt[N << 2];
vector<int> son[N << 2], Stack;
void init() {
cnt = tim = scc = 0;
for (int i = 1; i <= n; ++ i) {
x[i][1] = ++ cnt;
son[cnt].clear();
dfn[cnt] = lt[cnt] = 0;
}
for (int i = 1; i <= n; ++ i) {
x[i][0] = ++ cnt;
son[cnt].clear();
dfn[cnt] = lt[cnt] = 0;
}
for (int i = 1; i <= n; ++ i) {
y[i][1] = ++ cnt;
son[cnt].clear();
dfn[cnt] = lt[cnt] = 0;
}
for (int i = 1; i <= n; ++ i) {
y[i][0] = ++ cnt;
son[cnt].clear();
dfn[cnt] = lt[cnt] = 0;
}
}
void tarjan(int u) {
dfn[u] = low[u] = ++ tim;
Stack.push_back(u);
for (int v : son[u]) {
if (! dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (! lt[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
lt[u] = ++ scc;
while (Stack.back() != u) {
lt[Stack.back()] = scc;
Stack.pop_back();
}
Stack.pop_back();
}
}
int main() {
scanf("%d", &T);
while (T --) {
scanf("%d", &n);
init();
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
scanf("%1d", &a[i][j]);
}
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
scanf("%1d", &b[i][j]);
c[i][j] = a[i][j] ^ b[i][j];
if (c[i][j]) {
son[x[i][1]].push_back(y[j][0]);
son[y[j][0]].push_back(x[i][1]);
son[x[i][0]].push_back(y[j][1]);
son[y[j][1]].push_back(x[i][0]);
}
else {
son[x[i][1]].push_back(y[j][1]);
son[y[j][1]].push_back(x[i][1]);
son[x[i][0]].push_back(y[j][0]);
son[y[j][0]].push_back(x[i][0]);
}
}
}
for (int i = 1; i <= cnt; ++ i) {
if (! dfn[i]) {
tarjan(i);
}
}
int fg = 0;
for (int i = 1; i <= n; ++ i) {
if (lt[x[i][1]] == lt[x[i][0]]) {
puts("NO");
fg = 1;
break;
}
if (lt[y[i][1]] == lt[y[i][0]]) {
puts("NO");
fg = 1;
break;
}
}
if (! fg) puts("YES");
}
return 0;
}
作者:yifan0305
出处:https://www.cnblogs.com/yifan0305/p/17346853.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
转载时还请标明出处哟!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】