• 2-SAT


    2-SAT

    n n n 个命题,每个命题只有两个变量,每个变量要么是 1 1 1(真) 要么是 0 0 0(假)。

    询问是否有合法的构造使得所有的命题的与成立。

    1. a → b    ⟺    ¬ a ∨ b a \rightarrow b \iff \neg a \vee b ab¬ab ( → \rightarrow 是蕴含,若 p 则 q的意思)
    2. a → b    ⟺    ¬ b → ¬ a a \rightarrow b \iff \neg b \rightarrow \neg a ab¬b¬a (原命题和逆否命题等价)
    3. x x x ¬ x \neg x ¬x 在同一个 SCC 中 无解。(3是有解的必要条件)
    4. 若上述条件成立,一定有解吗?(证明充分性)
      1. 答案:一定有。
      2. 发现,如果有 a → x 1 → x 2 → . . . → b a \rightarrow x_1 \rightarrow x_2 \rightarrow ... \rightarrow b ax1x2...b , b → y 1 → y 2 → . . . → a b \rightarrow y_1 \rightarrow y_2 \rightarrow ... \rightarrow a by1y2...a 。那么根据逆否命题一定有: ¬ a ← x 1 ← x 2 ← . . . ← ¬ b \neg a \leftarrow x_1 \leftarrow x_2 \leftarrow ... \leftarrow \neg b ¬ax1x2...¬b , ¬ b ← y 1 ← y 2 ← . . . ← ¬ a \neg b \leftarrow y_1 \leftarrow y_2 \leftarrow ... \leftarrow \neg a ¬by1y2...¬a 。所以如果 a , b a,b a,b 在同一个 SCC 中, ¬ a , ¬ b \neg a ,\neg b ¬a,¬b 一定也在同一个 SCC 中。
      3. 每个 SCC 中的变量要么都选,要么都不选。每个SCC一定有一个和他对偶的SCC。
      4. 构造方式:先 tarjan 缩点,进行拓扑排序,对于一个变量的两种情况,取更靠后的那一种。

    命题条件总结:

    1. a ∨ b    ⟺    ¬ a → b    ⟺    ¬ b → a a \vee b \iff \neg a \rightarrow b \iff \neg b \rightarrow a ab¬ab¬ba
    2. a → b    ⟺    ¬ a ∨ b    ⟺    ¬ b ∨ a a \rightarrow b \iff \neg a \vee b \iff \neg b \vee a ab¬ab¬ba
    3. a = 1    ⟺    a ∨ a a = 1 \iff a \vee a a=1aa
    4. a = 0    ⟺    ¬ a ∨ ¬ a a = 0 \iff \neg a \vee \neg a a=0¬a¬a

    连边的方式:

    1. i 对应 i+n , 在模板题中有:
    while (m -- )
    {
        int i, a, j, b;
        scanf("%d%d%d%d", &i, &a, &j, &b);
        // i,i+n ; j,j+n
        add(i + n * (a & 1), j + n * (b ^ 1));
        add(j + n * (b & 1), i + n * (a ^ 1));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 2 * i 为真,2 * i + 1是假
    add(2 * i + !a, 2 * j + b);
    add(2 * j + !b, 2 * i + a);
    
    • 1
    • 2

    因为tarjan得到的id是拓扑逆序,最后正序遍历就行了。

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    const int N = 2000010, M = 2000010;
    
    int n, m;
    int h[N], e[M], ne[M], idx;
    int dfn[N], low[N], ts, stk[N], top;
    int id[N], cnt;
    bool ins[N];
    
    void add(int a, int b)
    {
        e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
    }
    
    void tarjan(int u)
    {
        dfn[u] = low[u] = ++ ts;
        stk[ ++ top] = u, ins[u] = true;
        for (int i = h[u]; ~i; i = ne[i])
        {
            int j = e[i];
            if (!dfn[j])
            {
                tarjan(j);
                low[u] = min(low[u], low[j]);
            } else if (ins[j]) low[u] = min(low[u], dfn[j]);
        }
    
        if (low[u] == dfn[u])
        {
            int y;
            cnt ++ ;
            do
            {
                y = stk[top -- ], ins[y] = false, id[y] = cnt;
            } while (y != u);
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        memset(h, -1, sizeof h);
    
        while (m -- )
        {
            int i, a, j, b;
            scanf("%d%d%d%d", &i, &a, &j, &b);
            // i,i+n ; j,j+n
            add(i + n * (a & 1), j + n * (b ^ 1));
            add(j + n * (b & 1), i + n * (a ^ 1));
        }
    
        for (int i = 1; i <= n * 2; i ++ )
            if (!dfn[i])
                tarjan(i);
    
        for (int i = 1; i <= n; i ++ )
            if (id[i] == id[i + n])
            {
                puts("IMPOSSIBLE");
                return 0;
            }
    
        puts("POSSIBLE");
        for (int i = 1; i <= n; i ++ )
            if (id[i] < id[i + n]) printf("1 ");
            else printf("0 ");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
  • 相关阅读:
    复制远程连接到Linux使用VIM打开的内容到Windows
    【附源码】计算机毕业设计SSM图书管理系统
    leetcode:101.对称二叉树
    corosync+pacemaker+web集群
    【java面试题】Redis多线程模型怎么理解,那它会有线程安全问题吗?
    《数据库系统概论》:DBA的职责有些
    2022秋季快手面试经历
    九九重阳节文案、海报 || 九九重阳 情意绵绵
    JVM可视化工具之Java VisualVM
    我对Chat-GPT4o的使用感受
  • 原文地址:https://blog.csdn.net/hacker__man/article/details/126212538