2-SAT问题

2-SAT问题

现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x]AND A[y]=0、A[x] OR A[y] OR A[z]=1等,要确定A[0..N-1]的值,使得其满足所有限制关系。这个称为SAT问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题。

由于在2-SAT问题中,最多只对两个元素进行限制,所以可能的限制关系共有11种:

A[x]

NOT A[x]

A[x] AND A[y]

A[x] AND NOT A[y]

A[x] OR A[y]

A[x] OR NOT A[y]

NOT (A[x] AND A[y])

NOT (A[x] OR A[y])

A[x] XOR A[y]

NOT (A[x] XOR A[y])

A[x] XOR NOT A[y]

进一步,A[x] ANDA[y]相当于(A[x]) AND (A[y])(也就是可以拆分成A[x]与A[y]两个限制关系),NOT(A[x]OR A[y])相当于NOT A[x] AND NOT A[y](也就是可以拆分成NOT A[x]与NOT A[y]两个限制关系)。因此,可能的限制关系最多只有9种。

在实际问题中,2-SAT问题在大多数时候表现成以下形式:有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。

(引自:http://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html)

在程序实现中,我们把初始的n个物品变成2n个节点,然后从0开始编号到2*n-1号。其中原始第i个物品对应节点i*2和i*2+1。如果我们mark[i*2]节点,那么表示我们i节点设为假,如果我们mark[i*2+1]节点,那么我们i节点设为真。同一个节点只能mark一种结果(即对于原始i来说,我们只能mark[i*2]或mark[i*2+1]其中之一)。

然后加入存在i假或j假的论述,我们就引一条图中从2*i+1到2*j的边,再引一条2*j+1到2*i的边,表示如果i是真的,那么j肯定是假的(否则之前的结论不成立)。且如果j是真的,那么i肯定是假的(否则之前的结论也不成立)。

如果存在i为真的论述,那么我们直接mark[i*2+1]即可。

最终判断整个问题是否有解,就是做多次dfs来设置每个节点可能的值(真或假),看看是否所有可能取值情况都会冲突。如果不冲突,那么有解。(这里并不需要暴力枚举所有的可能,具体请看刘汝佳<<训练指南>>P323)

注意:下面解题的过程中,比如我们要设定i为假,那么我们不是mark[i*2]=true,而是添加一条i*2+1->i*2的边,即只要i设为真了,那么就会使得导出矛盾。因为每个节点只有两种选择,所以上面添加边的思路更直观。反而每次都去转换成一条或语句更不直观。我基本上所有的题解都是以添加边的角度来做的。

下面给出2-SAT的模板代码:

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=10000+10;
struct TwoSAT
{
    int n;//原始图的节点数(未翻倍)
    vector<int> G[maxn*2];//G[i]==j表示如果mark[i]=true,那么mark[j]也要=true
    bool mark[maxn*2];//标记
    int S[maxn*2],c;//S和c用来记录一次dfs遍历的所有节点编号

    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)条件
    //xval=0表示假,yval=1表示真
    void add_clause(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执行dfs遍历,途径的所有点都标记
    //如果不能标记,那么返回false
    bool dfs(int x)
    {
        if(mark[x^1]) return false;//这两句的位置不能调换
        if(mark[x]) return true;
        mark[x]=true;
        S[c++]=x;
        for(int i=0;i<G[x].size();i++)
            if(!dfs(G[x][i])) return false;
        return true;
    }

    //判断当前2-SAT问题是否有解
    bool solve()
    {
        for(int i=0;i<2*n;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应用

POJ 3207 Ikki‘s Story IV - Panda‘s Trick(2-SAT):圆内外连线问题。解题报告!

POJ 3678 Katu Puzzle(2-SAT):与或非判断数值。解题报告!

POJ 3683 Priest John‘s Busiest Day(2-SAT输出方案):判断区间是否重叠。解题报告!

POJ 3648 Wedding(2-SAT):婚礼座位安排。解题报告!

POJ 2723 Get Luffy Out(2-SAT):钥匙开门问题。解题报告!

POJ 2749 Building roads(2-SAT):如何构建道路。解题报告!

POJ 2296 Map Labeler(2-SAT):放置正方形。解题报告!

HDU 3062 Party(2-SAT简单题):夫妻选择出席聚会。解题报告!

HDU 1814 Peaceful Commission(2-SAT:最小字典序):代表参加会议。解题报告!

HDU 3622 Bomb Game(2-SAT+二分):放置炸弹问题。解题报告!

HDU 3715 Go Deeper(2-SAT):程序递归深度问题。解题报告!

HDU 1824 Let‘s go home(2-SAT):队员去留问题。解题报告!

POJ 3905 Perfect Election(简单2-SAT):选举问题。解题报告!

HDU 4115 Eliminate the Conflict (2-SAT):出拳问题。解题报告!

时间: 2024-11-10 12:03:37

2-SAT问题的相关文章

LA 3211 飞机调度(2—SAT)

https://vjudge.net/problem/UVALive-3211 题意: 有n架飞机需要着陆,每架飞机都可以选择“早着陆”和“晚着陆”两种方式之一,且必须选择一种,第i架飞机的早着陆时间为E,晚着陆时间为L,不得在其他时间着陆.你的任务是为这些飞机安排着陆方式,使得整个着陆计划尽量安全.换句话说,如果把所有飞机的实际着陆时间按照从早到晚的顺序排列,相邻两个着陆时间间隔的最小值. 思路: 二分查找最大值P,每次都用2—SAT判断是否可行. 1 #include<iostream>

8.3吝啬SAT问题

吝啬SAT问题是这样的:给定一组子句(每个子句都是其中文字的析取)和整数k,求一个最多有k个变量为true的满足赋值--如果该赋值存在.证明吝啬SAT是NP-完全问题. 1.易知吝啬SAT的解可以在多项式时间内验证,因此属于NP问题. 2.如果我们把吝啬SAT问题中的k设置为输入的数目,那么SAT问题就可以规约到吝啬SAT问题,所以吝啬SAT问题是np-完全问题.

POJ 3207 Ikki&#39;s Story IV - Panda&#39;s Trick(2 - sat啊)

题目链接:http://poj.org/problem?id=3207 Description liympanda, one of Ikki's friend, likes playing games with Ikki. Today after minesweeping with Ikki and winning so many times, he is tired of such easy games and wants to play another game with Ikki. liy

多边形碰撞 -- SAT方法

检测凸多边形碰撞的一种简单的方法是SAT(Separating Axis Theorem),即分离轴定理. 原理:将多边形投影到一条向量上,看这两个多边形的投影是否重叠.如果不重叠,则认为这两个多边形是分离的,否则找下一条向量来继续投影.我们不需要比较很多条向量,因为已经在数学上证明,多边形每条边的垂直向量就是我们需要的向量. 1.AABB 让我们首先以AABB开始(AABB是一种两边分别平行于X-Y轴的矩形) 判断两个AABB是否碰撞,我们只需要投影两次,分别是投影在平行于X轴和Y轴的向量上

hdu 4751 Divide Groups 2—sat问题 还是未理解

Divide Groups Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1443    Accepted Submission(s): 512 Problem Description This year is the 60th anniversary of NJUST, and to make the celebration mor

HIT 1917 2—SAT

题目大意:一国有n个党派,每个党派在议会中都有2个代表, 现要组建和平委员会,要从每个党派在议会的代表中选出1人,一共n人组成和平委员会. 已知有一些代表之间存在仇恨,也就是说他们不能同时被选为和平委员会的成员, 现要你判断满足要求的和平委员会能否创立?如果能,请任意给出一种方案.( POI 0106 ) ----------------------------------------- 这道题就是裸的2-SAT 不过我用了tarjan缩点 然后一波拓排 tips:一个问题无解当且仅当一个集合的

2—SAT问题

现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x] AND A[y]=0.A[x] OR A[y] OR A[z]=1.A[x] XOR A[y]=0等,要确定A[0..N-1]的值,使得其满足所有限制关系.这个称为SAT问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题. 对于x.y有11种关系,将其拆点2x(假),2x+1(真).对于x为假或者y为假这样的条件,我们连两条有向边2x+1->2y.2y+1->2x,注意这里是有向边以及边的起点和终点的关

SAT考试里最难的数学题? &middot; 三只猫的温暖

问题 今天无意中在Quora上看到有人贴出来一道号称是SAT里最难的一道数学题,一下子勾起了我的兴趣.于是拿起笔来写写画画,花了差不多十五分钟搞定.觉得有点意思,决定把解题过程记下来.原帖的图太小,我用GeoGebra重新画了一遍.没错,我就是强迫症. 为了省事,就把这道题叫做RASBTC. In the figure above, arc (text{SBT}) is one quarter of a circle with center (text{R}) and radius 6. If

SAT求解器变元活跃度计算模式的切换

变元活跃度计算模式有:VSIDS.基于历史出现时刻与当前冲突时刻距离等 有三个最小堆: // A priority queue of variables ordered with respect to the variable activity. Heap<VarOrderLt> order_heap_CHB,                                order_heap_VSIDS, order_heap_distance; 一般的求解切换方式是: conflicts

SAT求解器快速入门

从事组合优化或信息安全方向的研究人员,基于SMT研究,需要快速学习SAT求解器的基本原理和方法.以下是快速入门的几点体会: 1.理清需要——是完备求解器还是不完备求解器 完备求解器 能给出SAT.UNSAT.unknow三种确定的答案,尤其是UNSAT结论能给出证明推导过程,指出导致矛盾的关键路径.   不完备求解器能给出SAT.unknow两种结论.SAT结论给出一组可满足解即完成求解任务.可能会存在其他一组或多组可满足解.如果问题本身不知一组解,那么不同的求解器得出的可满足解可能不同. 2.