2-SAT详解

来源:互联网 发布:java属性是什么意思 编辑:程序博客网 时间:2024/06/06 12:37

以下内容摘自《算法竞赛入门经典训练指南》:

2-SAT问题是这样的:有n个布尔变量xi,另有m个需要满足的条件,每个条件的形式都是“xi为真/假或者xj为真/假“

2-SAT的解法有多种不同的叙述方式,这里采用一种比较容易理解的,且效率也不错的方式。构造一张有向图,其中每个变量xi拆分成两个结点2i和2i + 1,分别表示xi为假和xi为真,最后要为每个变量选其中的一个结点进行标记,比如标记了2i,表示xi为假,标记了2i + 1表示xi为真了

对于“xi为假或者xj为假“这种条件,就连一条有向边,2i + 1->2j,表示如果标记了xi为真,那么xj就必须要为假,同理,还得另外一条有向边2j + 1-> 2i,表明如果xj为真了,xi必须为假,这样才能满足上述的条件

接下来考虑一下每个没有赋值的变量,设为xi。先假设他为假,然后标记结点2i,接着沿着有向边标记所有能标记的点,如果标记过程中发现某个变量对应的两个结点都被标记,则“xi为假“这个假设是不成立的,需要改成“xi为真“,然后重新标记。注意,这个算法没有回溯过程,如果当前考虑的变量不管赋值为真还是为假都会引起矛盾的话,那就可以证明整个2-SAT问题无解(即使调整以前赋值的其他变量也没用)

算法模版:

#include <cstdio>#include <cstring>#include <vector>using namespace std;const int MAXNODE = 5010 * 2;struct TwoSat{    int n, top;    vector<int> G[MAXNODE];    bool mark[MAXNODE];    int Stack[MAXNODE];    void init(int n) {        this->n = n;        for (int i = 0; i < 2 * n; i++)            G[i].clear();        memset(mark, 0, sizeof(mark));    }    //x = xval或者y = yval,这个表示或的关系    void AddClause(int x, int xval, int y, int yval) {        x = x * 2 + xval;        y = y * 2 + yval;        G[x ^ 1].push_back(y);        G[y ^ 1].push_back(x);    }    //x = xval的时候,y必须等于yval,这个表示且的关系    void AddLimit(int x, int xval, int y, int yval) {        x = x * 2 + xval;        y = y * 2 + yval;        G[x].push_back(y);    }    bool dfs(int u) {        //当前需要标记的结点是u,如果u ^ 1这个结点已经被标记过了,就表示两个结点都要被标记掉,明显冲突了        if (mark[u ^ 1]) return false;        if (mark[u]) return true;        mark[u] = true;        //模拟栈        Stack[++top] = u;        //和u相邻的点都进行标记,以判断是否出错        for (int i = 0; i < G[u].size(); i++)             if (!dfs(G[u][i])) return false;        return true;    }    bool solve() {        for (int i = 0; i < 2 * n; i += 2)             //如果在其他点的标志过程中,其中一个点被标记了,就无需在枚举了,因为在dfs的过程中,和其相连的点也被标记了,较少了重复枚举            if (!mark[i] && !mark[i ^ 1]) {                top = 0;                //假设xi为假是错误的,这时候就要进行回溯,将dfs过程中标记过的点还原,再判断对错                 if (!dfs(i)) {                    while (top) mark[Stack[top--]] = false;                    if (!dfs(i ^ 1)) return false;                }            }        return true;    }}Two;int main() {    return 0;}

习题的话,可以在本博客找,我有进行分类的

0 0