2-SAT问题
2-SAT问题
有 \(n\) 个布尔变量 $x_1 \sim x_n $,另有 \(m\) 个需要满足的条件,每个条件的形式都是 「\(x_i\) 为 true
/ false
或 \(x_j\) 为 true
/ false
」。比如 「\(x_1\) 为真或 \(x_3\) 为假」、「\(x_7\) 为假或 \(x_2\) 为假」。
2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。
求出构造出来的解(若无解输出POSSIBLE
)
输入格式
第一行两个整数 \(n\) 和 \(m\),意义如题面所述。
接下来 \(m\) 行每行 \(4\) 个整数 \(i\), \(a\), \(j\), \(b\),表示 「\(x_i\) 为 \(a\) 或 \(x_j\) 为 \(b\)」(\(a, b\in \{0,1\}\))
输出格式
如无解,输出 IMPOSSIBLE
;否则输出 POSSIBLE
。
下一行 \(n\) 个整数 \(x_1\sim x_n\)(\(x_i\in\{0,1\}\)),表示构造出的解。
样例 #1
样例输入 #1
3 1
1 1 3 0
样例输出 #1
POSSIBLE
0 0 0
解法
我们考虑建图,把 \(x_1\) 为 false
设成 \(1\) 号点,\(x_1\) 为 true
设成 \(n + 1\) 号点。
假如一个条件中可以建成一些边:
例如条件a 0 b 0
就代表要么a = false
要么b = false
那么很容易可以知道
如果 a = true 那么一定有 b = false
如果 b = true 那么一定有 a = false
\(x_{a+n}\) 代表 a = true
\(x_{b}\) 代表 b = false
我们就建一条边由 \(x_{a+n}\) 指向 \(x_b\)
这条边就代表选择\(x_{a+n}\) 就必须选 \(x_b\)
无解的情况:
某一个变量的 false 能走到 true,从 true 也能走到 false,就说明无解。
为什么一个变量的 false(true) 能走到一个变量的 true(false) 不是无解的情况呢?
我们考虑边的意义:
选了这个变量的 false 就必须选这个变量的 true
那就选这个变量的 true不就好了!
这也是我们如何找出解的方法:
找出解
我们把每个点的 false 都 dfs 一次,
如果找不到这个点的 true,那就代表这个点可以选 false,
若找到了这个点的 true,则再在 true 上dfs一次,
如果 dfs 找不到 false,则代表这个点选true,
如果也 dfs 到了 false,那就证明无解。
这样的复杂度是\(O(n^2)\) 显然不能过
那怎么办呢?
我们考虑
tarjan缩点
某一个变量的 false 能走到 true,从 true 也能走到 false,就说明无解。
这就说明一个变量的 false 与 true 在一个强连通分量里面
tarjan 可以很好解决这个问题
for(int i = 1;i <= n; ++i){
if(c[i] == c[i + n]){
return 0;
}
}
那怎么找出解呢?
我们考虑
情况1:当一个点的 false 与 true 不会互相到达(指向),那就可以随便取
情况2:当一个点的 false 可以到达(指向) true,那就只能取 true
情况3:当一个点的 true 可以到达(指向) false,那就只能取 false
相互可以到达(就上面说的情况)就无解。
我们首先判断出来这有没有解,如果有解,那么情况二和情况三不可能都满足
我们现在所需要看的就是到底满足哪一个情况?
首先因为没有false和true是同一个强联通分量,所以
当一个点的 false 可以到达(指向) true,那么这个点的 false 在tarjan(dfs)里面必定被先找到,c[false] 也就比 c[true] 更大
当一个点的 true 可以到达(指向) false,那么这个点的 true 在tarjan(dfs)里面必定被先找到,c[true] 也就比 c[false] 更大
当两个点没关系的时候,可以随便取
for(int i = 1;i <= n; ++i){
if(c[i] > c[i + n]) printf("1 ");
else printf("0 ");
}