2-SAT

  有N个布尔变量xi,另有M个需要满足的条件,每个条件的形式都是xi为真/假或xj为真/假。2-SAT问题的目标是给每个变量赋值,使所有条件得到满足。

  把每个变量xi拆成两个节点2i和2i+1,分别表示xi为假和xi为真,这两个只能有一个被标记。对于xi为假或xj为假这样的条件,连一条有向边从2i+1->2j,2j+1->2i,表示如果xi为真的话xj必须为假,xj为真的话xi必须为假。建完图之后搜索,先假设xi为假,标记2i,DFS把和2i相连的点都标记,中途如果遇到遇到j和j^1都标记了说明矛盾了,返回false。如果把xi标记为假失败,再尝试把xi标记为真,重新搜索。如果真假都不行则无解。

struct TwoSAT{
    int n,c,S[MAXN*2];
    vector<int> G[MAXN*2];
    bool mark[MAXN*2];
    void init(int n){
        this->n=n;
        for(int i=0;i<n*2;i++) G[i].clear();
        memset(mark,0,sizeof(mark));
    }
    bool dfs(int x){
        if(mark[x^1]) return false;
        if(mark[x]) return true;
        mark[x]=true;
        S[c++]=x;
        int L=G[x].size();
        for(int i=0;i<L;i++) if(!dfs(G[x][i])) return false;
        return true;
    }
    //x为假或y为假
    void add_clause(int x,int xval,int y,int yval){
        x=x*2+xval; //x为假时的值
        y=y*2+yval; //y为假时的值
        G[x^1].push_back(y);  //x为真时y必须为假
        G[y^1].push_back(x);  //y为真时x必须为假
    }
    bool solve(){
        //先假设x[i]为假,如果不行再判断x[i]为真是否可以,如果还是不行返回false
        for(int i=0;i<n*2;i+=2) if(!mark[i]&&!mark[i+1]){
            c=0;
            if(!dfs(i)){
                while(c>0) mark[S[--c]]=false;
                if(!dfs(i+1)) return false;
            }
        }
        return true;
    }
}



你可能感兴趣的:(2-SAT)