Loading

2-SAT

1 2-SAT问题

有n给变量\(x_i\),每一个变量有两个可能的取值\(x_{i,0},x_{i,1}\),显然,总共的方案数为\(2^n\)

有m个约束条件,每一个约束条件形如:如果\(x_i\)赋值为\(x_{i,p}\),则\(x_j\)赋值为\(x_{j,q}\)。问在这m个约束条件下,是否存在一种合法的方案,如果有,则请构造出来。

2 求解

对于每一个变量,建立两个点,\(a_i,a_{i+n}\),表示选\(x_i\)还是\(x_{i+n}\),对于每一个约束条件,则从\(a_{i+p*n}\)\(a_{j+q*n}\)连边,不要忘了逆否命题,即从\(a_{j+(1-q)*n}\)\(a_{i+(1,p)*n}\)连边。

连完后,求这个有向图的强联通分量,若发现\(a_i\)\(a_{i+n}\)在同一个强联通分量里,则不存在一个合法的方案,否则存在。

构造方案,只需要判断\(a_i\)及其对应点\(a_{i+n}\)的强联通分量谁大,即可判断是取值选0还是选1,这是因为他tarjan在求解强联通分量时,强联通分量的编号已经是从后往前的拓扑序了,这是由于我们使用的栈的缘故。

实际上,我们构造解,需要缩点,从后往前拓扑排序,即建反图执行拓扑排序,然后赋值,无入度点为0,然后下面为1,再为0,以此类推。

上面说的并不是非常严谨。

综上,当我们不用经过上述过程,直接判断即可,当\(c[a[i]]>c[a[opp[i]]],opp[i]=i+n\)时,赋值为1,否则赋值为0

这个构造过程其实可以这样理解,由于拓扑序大的对拓扑序小的有限制,所以我们优先选拓扑序小的。

注意,上面是 \(0\),下面是 \(1\)

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 2001000
#define M 2000100
using namespace std;

const int INF=0x3f3f3f3f;

inline int Min(int a,int b){
    return a>b?b:a;
}

inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct edge{
    int to,next;
    inline void intt(int to_,int ne_){
        to=to_;next=ne_;
    }
};
edge li[M];
int head[N],tail;

inline void add(int from,int to){
    li[++tail].intt(to,head[from]);
    head[from]=tail;
}

int n,m,x[N][2];

int dfn[N],low[N],c[N],tot,ctot,stack[N],top,opp[N];
bool ins[N];
vector<int> scc[N];

inline void tarjan(int k){
    dfn[k]=low[k]=++tot;
    stack[++top]=k;ins[k]=1;
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(!dfn[to]){
            tarjan(to);
            low[k]=Min(low[k],low[to]);
        }
        else if(ins[to]) low[k]=Min(low[k],dfn[to]);
    }
    if(dfn[k]==low[k]){
        ctot++;int val;
        do{
            val=stack[top--];c[val]=ctot;
            ins[val]=0;scc[ctot].push_back(val);
        }while(val!=k);
    }
}

int val[N];

int main(){
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int a=read(),b=read(),c=read(),d=read();
        add(a+(1-b)*n,c+d*n);add(c+(1-d)*n,a+b*n);
//        printf("line1:%d %d\n",a+(1-b)*n,c+d*n);
//        printf("line2:%d %d\n",c+(1-d)*n,a+b*n);
        x[a][1]=1;x[a][0]=0;x[c][1]=1;x[c][0]=0;
    }
    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]){
            printf("IMPOSSIBLE\n");
            return 0;
        }
        opp[i]=n+i;opp[n+i]=i;
    }
    printf("POSSIBLE\n");
    for(int i=1;i<=2*n;i++) val[i]=c[i]>c[opp[i]];
//    for(int i=1;i<=2*n;i++) printf("%d ",val[i]);
//    printf("\n");
    for(int i=1;i<=n;i++) printf("%d ",x[i][val[i]]);
    return 0;
}
posted @ 2021-04-11 14:31  hyl天梦  阅读(47)  评论(0编辑  收藏  举报