• 2-SAT (two-statisfiability) 算法 学习笔记


    $2-SAT$问题指的是对于若干限制求出一组可行解的问题。

    考虑对于$n$个值域为${0,1}$的变量$x_1 , x_2 ,...,x_n$ 满足若干限制:

    若 $x_i = p$ 则 $x_j = q ( i,jin[1,n],p,q in {0,1})$

    我们考虑对于每一个变量$x_i$开一个值域$x_{i,0} , x_{i,1}$表示第$i$个变量取值为$0/1$的点。

    然后考虑每一组命题, 若 $x_i = p$ 则 $x_j = q , i,jin[1,n],p,q in {0,1}$ ,

    首先把$x_{i,p}$和$x_{j,q}$两个点连一条单向边。

    并且 由于这个命题非常特殊,值域大小只为2 , 其逆否命题也是限制(可以显然的反证)。

    于是就把$x_{j,1-q}$和$x_{i,1-p}$ 连一条单向边。

    然后对于上面的图跑tarjan找出scc,如果$x_{i,0}$和$x_{i,1}$在同一连通块中了,

    说明下列命题成立: “若$x_i = 0$则$x_i = 1$ ” ,“若$x_i = 1$则$x_i = 0$ ” 所以此时答案无解。

    如何构造出一组解呢,由于tarjan的dfs特性,

    设$c_i$表示通过tarjan算法求出的连通块编号(这本身就是逆拓扑序的)。

    $val_i = c_{x_{i,0}} > c_{x_{i,1}}$  就构造出$val_i , iin [1,n]$一组合法解了。

    P4782 【模板】2-SAT 问题

    设变量$x_i in {0,1}$ ,给出若干组关系:$x_i = p $ 或者 $x_j = q$ 

    如果$x_i , (iin [1,n])$有解,先输出"POSIBLE"然后输出一组合法解。

    如果$x_i ,  (iin [1,n])$无解,则输出"IMPOSIBLE"即可。

    对于100%的数据$n,mleq 10^6$

    Sol: 

    $x_i$为$p$ 或 $x_j$为$q$ 可以拆成$2 imes 2$对逻辑关系。

    • 若$x_i$为$1-p$,则$x_j$为$q$ , 若$x_j$为$1-q$,则$x_i$为$p$
    • 若$x_j$为$1-q$,则$x_i$为$p$ , 若$x_i$为$1-p,$ 则$x_j$为$q$

    实现方面,只需:$[1,N]$为原来元素的$0$域,$[N+1,2N]$ 为原来元素的$1$域.

    # include <bits/stdc++.h>
    using namespace std;
    const int N=2e6+10,M=4e6+10;
    struct rec{ int pre,to;}a[M];
    stack<int>s;
    bool ins[N];
    int cnt,tot,n,m,head[N],c[N],dfn[N],low[N],val[N];
    void adde(int u,int v)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        head[u]=tot;
    }
    void tarjan(int u)
    {
        dfn[u]=low[u]=++dfn[0];
        s.push(u);ins[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (!dfn[v]) {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            } else if (ins[v]) low[u]=min(low[u],dfn[v]);
        }
        if (dfn[u]==low[u]) {
            cnt++; int v;
            do {
                v=s.top(); s.pop();
                ins[v]=0; c[v]=cnt;
            } while (u!=v);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++) {
            int s,ps,t,pt; scanf("%d%d%d%d",&s,&ps,&t,&pt);
            adde(s+(1-ps)*n,t+pt*n); adde(t+(1-pt)*n,s+ps*n);
            adde(t+(1-pt)*n,s+ps*n); adde(s+(1-ps)*n,t+pt*n);
        }
        for (int i=1;i<=2*n;i++) if (!dfn[i]) tarjan(i);
        for (int i=1;i<=n;i++) if (c[i]==c[i+n]) {
            puts("IMPOSSIBLE"); return 0;
        }
        for (int i=1;i<=n;i++) val[i]=c[i]>c[n+i];
        puts("POSSIBLE");
        for (int i=1;i<=n;i++) printf("%d ",val[i]); puts("");
        return 0;
    }
  • 相关阅读:
    python面试题
    面试总结
    552 Student Attendance Record II 学生出勤记录 II
    551 Student Attendance Record I 学生出勤纪录 I
    547 Friend Circles 朋友圈
    546 Remove Boxes 移除盒子
    543 Diameter of Binary Tree 二叉树的直径
    542 01 Matrix 01 矩阵
    3.1 特性
    2.6 datetime 模块
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/11295659.html
Copyright © 2020-2023  润新知