嘘~ 正在从服务器偷取页面 . . .

2–SAT 学习笔记


2–SAT 学习笔记

现在 2–SAT 已经是蓝题了吗。

由于我的模板都不知道是多少年前写的了,所以就有了这边文章。

2–SAT 问题

模板题链接:P4782 【模板】2–SAT

题意

有 $n$ 个布尔变量 $x_1,x_2,\cdots,x_n$,另有 $m$ 个需要满足的条件,每个条件的形式都是 「$x_i$ 为 true / false 或 $x_j$ 为 true / false」。比如 「$x_1$ 为真或 $x_3$ 为假」、「$x_7$ 为假或 $x_2$ 为假」。

2–SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

输入格式

第一行两个整数 $n$ 和 $m$,意义如题面所述。

接下来 $m$ 行每行 $4$ 个整数 $i,a,j,b$,表示 「$x_i$ 为 $a$ 或 $x_j$ 为 $b$」$(a, b\in \{0,1\})$

输出格式

如无解,输出 IMPOSSIBLE;否则输出 POSSIBLE

下一行 $n$ 个整数 $x_1,x_2,\cdots, x_n~(x_i\in\{0,1\})$,表示构造出的解。

数据范围

$1\leq n, m\leq 10^6$

SAT 问题(satisfiability problem)指解决给定限制下,若干布尔变量的可满足性问题。

例如我想要知道

对于布尔变量 $a,b,c$ 的一种可行方案

这里出现了 $3$ 个变量,即这是一个 3–SAT 问题;而本题只有两个变量和 $m$ 条限制,即 2–SAT 问题。

然而除了 2–SAT 问题以外的 SAT 问题均为 NPC 问题,因此关于 SAT 问题这部分的介绍就到此为止了。

现在考虑怎么做本题。我们把每个点看作 $x$ 和 $\lnot x$ ,不妨编号为 $x$ 和 $x + n$ 。

那么对于每条限制,我们可以转化为图上的一条边

对应的代码就是:

while(m--)
{
    static int i, j; static bool a, b;
    cin >> i >> a >> j >> b;
    vec[i + n * a].pb(j + n * (b ^ 1));
    vec[j + n * b].pb(i + n * (a ^ 1));
}

注意到存在某个点 $x$ 满足 $x$ 和 $\lnot x$ 在同一个强连通分量中,一定无解。

对于有解的情况,我们取命题「 $x$ 的拓扑序大于 $\lnot x$ 的拓扑序」的值即可,这里拓扑序指缩点后的拓扑序。

不过由于 Tarjan 缩点以后每个点的编号正好是逆拓扑序,我们用小于判断即可。

时间复杂度 $\mathcal{O}(n + m)$

代码:(注意空间要开两倍)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define pb push_back
#define N ((int)(2e6 + 15))

bool ins[N];
vector<int> vec[N];
int stktop, scnt, stk[N], low[N], dfn[N], scc[N], top[N]; 
void Tarjan(int u)
{
    static int tim = 0; dfn[u] = low[u] = ++tim;
    stk[++stktop] = u; ins[u] = true;
    for(int v : vec[u])
    {
        if(!dfn[v]) { Tarjan(v); down(low[u], low[v]); }
        else if(ins[v]) down(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        top[++scnt] = u;
        for(int p = 0; p != u; )
        { 
            p = stk[stktop--];
            ins[p] = false; scc[p] = scnt;
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n, m; cin >> n >> m;
    while(m--)
    {
        static int i, j; static bool a, b;
        cin >> i >> a >> j >> b;
        vec[i + n * a].pb(j + n * (!b));
        vec[j + n * b].pb(i + n * (!a));
    }
    rep(i, 1, n * 2) if(!dfn[i]) Tarjan(i);
    rep(i, 1, n) if(scc[i] == scc[i + n]) return cout << "IMPOSSIBLE\n", 0;
    cout << "POSSIBLE\n";
    rep(i, 1, n) cout << (scc[i] < scc[i + n]) << " \n"[i == n];
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录