2-SAT 1

2-SAT 模板

n 个布尔变量 x1xn
另有 m 个需要满足的条件,每个条件的形式都是 「xitrue / falsexjtrue / false」。
比如 「x1 为真或 x3 为假」、「x7 为假或 x2 为假」。
2SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

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

判解

图论的一个非常重要的内容就是建图
我们把每个变量看成一个点,方程看作一条边
直接连边?显然会麻烦好多
这里的做法是拆点,
就是xi1的情况和xi0的情况在图上对应两个点
而对应的,一个条件也会被拆成:
条件本身和它的逆否
从取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 @   2K22  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示