浅谈2-SAT问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44824275/article/details/97612032

2 S A T \qquad2-SAT 问题是这样的:有 n n 个布尔型变量 x i x_i ,另有 m m 个需要满足的条件,每个条件的形式都是“ x i x_i 为真/假或者 x j x_j 为真/假”。比如:“ x 1 x_1 为真或者 x 3 x_3 为假”、“ x 7 x_7 为假或者 x 2 x_2 为假”都是合法的条件。注意这里的“或”是指两个条件至少有一个是正确的,比如 x 1 x_1 x 3 x_3 一共有 3 3 种组合满足“ x 1 x_1 为真或者 x 3 x_3 为假”,即 x 1 x_1 x 3 x_3 真、 x 1 x_1 x 3 x_3 假、 x 1 x_1 x 3 x_3 假。 2 S A T 2-SAT 问题的目标是给每个变量赋值,使得所有的条件得到满足。

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

\qquad 对于“ x i x_i 为假或者 x j x_j 为假”这样的条件,我们连一条有向边 2 i + 1 2 j 2i+1\rightarrow 2j ,表示如果标记结点 2 i + 1 2i+1 那么也必须标记结点 j j (因为如果 x i x_i 为真,则必须 x j x_j 为假才能使条件成立)。这条有向边相当于“推导出”的意思。同理,还需要连一条有向边 2 j + 1 2 i 2j+1\rightarrow 2i 。对于其他情况,也可以类似连边。换句话说,每个条件对应两条“对称”的边1

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

struct Two_SAT
{
    int n,c,s[kmax<<1];
    vector<int> G[kmax<<1];
    bool mark[kmax<<1];

    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;
    }

    void init(int x)
    {
        this->n=x;
        for(int i=0;i<(n<<1);i++) G[i].clear();
        memset(mark,0,sizeof(mark));
    }

    ///x=xval or y=yval
    void all_clause(int x,int xval,int y,int yval)
    {
        x=(x<<1)+xval;
        y=(y<<1)+yval;
        G[x^1].push_back(y);
        G[y^1].push_back(x);
    }

    bool solve()
    {
        for(int i=0;i<(n<<1);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;
    }
};

参考文献:
刘汝佳,陈锋.算法竞赛入门经典——训练指南[M].清华大学出版社:北京,2018:323-324.


  1. 但有时会出现已知某个变量为某个取值的情况,导致整个图不对称。 ↩︎

猜你喜欢

转载自blog.csdn.net/weixin_44824275/article/details/97612032
今日推荐