Skip to content

2-SAT 笔记

一、定义

是或, 是与。

2-SAT 解决形如

(x1¬x2)(¬x1x3)...

的问题。

二、解决方法

我们把一个点 x 拆成两个状态:x0 表示 x=0x1 表示 x=1。在题目给出的限制中,显然这些状态是可以互相推理的。

  1. xy

x0y1y0x1

  1. ¬xy

x1y1y0x0

  1. x¬y

x0y0y1x1

  1. ¬x¬y

x1y0y1x0

如果用 a=1 表示式子中要求 x 为真,a=0 表示式子中要求 x 为假,b 表示式子中要求 y 的状况,那么观察可以得到:

xa1yb1yb1xa1

我们根据推理情况建有向图,跑强连通分量,如果存在 x0x1 在同一强连通分量内,则说明无解。

x1 所在的强连通分量的拓扑序在 x0 所在的强连通分量的拓扑序之后取 x 为真,Kosaraju 算法按拓扑序排好的,Tarjan 算法则是反着的拓扑序,要将 sccx1>sccx0 改为 sccx1<sccx0

cpp
cin >> n >> m;
for (int i = 1, x, a, y, b; i <= m; i++) {
    cin >> x >> a >> y >> b;
    add(x + n * (a & 1), y + n * (b ^ 1));
    add(y + n * (b & 1), x + n * (a ^ 1));
}
kosaraju();
for (int i = 1; i <= n; i++)
    if (scc[i] == scc[i + n]) {
        cout << "IMPOSSIBLE";
        return 0;
    }
cout << "POSSIBLE\n";
for (int i = 1; i <= n; i++)
    cout << (scc[i] > scc[i + n]) << " ";

三、建图优化

如果有这么一张 n2 的图:

2 号点为例,发现它要这样连边:

所以弄两班公交车,一班向右走,一班向左走:

因为 2 号点要去 5 号点及其左边的点,所以他在 13 号点上向左走的公交车;7 号点应该要被 2 号点及其左边的点去,8 号点应该被 3 号点及其左边的点去,所以将 23 号点对应的公交站 1011 号点和 78 号点连边。

剩下的点同理:

我们成功把边数优化到 O(n) 级别的(由于常数是 6,上图 n=4,所以看起来像是还更多边了,实则当 n 有一定大小时并不是)。

最近更新