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 ");
}
posted @ 2023-03-28 08:12  He_Zi  阅读(22)  评论(0编辑  收藏  举报