2-SAT 1

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\) 问题的目标是给每个变量赋值使得所有条件得到满足。

一种最直接的方式就是暴力搜,时间复杂度\(O(2^n)\)
已经被证明的就是\(k-SAT\)对于\(k>2\)的部分是\(NPC\)问题
但是\(2-SAT\)还是能做的

判解

图论的一个非常重要的内容就是建图
我们把每个变量看成一个点,方程看作一条边
直接连边?显然会麻烦好多
这里的做法是拆点,
就是\(x_i\)\(1\)的情况和\(x_i\)\(0\)的情况在图上对应两个点
而对应的,一个条件也会被拆成:
条件本身和它的逆否
从取\(0\)变量对应的那个点向取\(1\)的点连有向边
它的实际含义就是"如果取\(0\)的变量取\(0\),取\(1\)的变量必须取\(1\)"
这样的好处不仅是容易理解,我们还会发现:
如果两个对立的点在同一个强连通分量,显然无解
实际含义就是"如果这个点取一个值并且还要满足所有条件,那么这个点必须取这个值的反面"
于是我们只需要一遍\(Tarjan\),就可以解决这个问题

构造可行解

听论文说,构造一组可行解,
就是从缩完点以后的\(DAG\)上自底向上删点
用下一个才学的\(Tarjan\)性质,
就是\(SCC\)的编号越小,表示在\(DAG\)上越靠后
于是选\(SCC\)编号小的即可

模板

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int o=2222222*2;
int n,m;
struct Graph{
    struct edge{
        int t;
        int n;
    }p[o];
    int h[o],cnt;
    int d[o],l[o],s[o],v[o],c[o],top,sum,num;
    void add(int s,int t){
        cnt++;
        p[cnt].t=t;
        p[cnt].n=h[s];
        h[s]=cnt;
    }
    void Tarjan(int x){
        d[x]=l[x]=++sum;
        v[x]=1;
        s[++top]=x;
        for(int i=h[x];i;i=p[i].n){
            int y=p[i].t;
            if(!d[y]){
                Tarjan(y);
                l[x]=min(l[x],l[y]);
            }
            else if(v[y]){
                l[x]=min(l[x],d[y]);
            }
        }
        if(l[x]==d[x]){
            int y;
            num++;
            do{
                y=s[top--];
                v[y]=0;
                c[y]=sum;
            }while(y!=x);
        }
    }
    bool find(){
        for(int i=1;i<=n;i++){
            if(c[i]==c[i+n]){
                return 0;
            }
        }
        return 1;
    }
    void make(){
        for(int i=1;i<=n;i++){
            if(c[i]>c[i+n]){
                printf("1 ");
            }
            else{
                printf("0 ");
            }
        }
    }
}G;
int read(){
    int i=1,j=0;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-')i=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        j=j*10+ch-48;
        ch=getchar();
    }
    return i*j;
}
void in(){
    n=read(),m=read();
    int a,b,x,y;
    for(int i=1;i<=m;i++){
        a=read(),x=read();
        b=read(),y=read();
        G.add(a+(!x)*n,b+y*n);
        G.add(b+(!y)*n,a+x*n);
    }
}
void work(){
    for(int i=1;i<=2*n;i++){
        if(!G.d[i]){
            G.Tarjan(i);
        }
    }
    if(G.find()){
        puts("POSSIBLE");
        return ;
    }
    puts("IMPOSSIBLE");
    exit(0);
}
void out(){
    G.make();
}
int main(){
    in();
    work();
    out();
    return 0;
}
posted @ 2022-08-19 10:34  2K22  阅读(49)  评论(0)    收藏  举报