一、定义

k-SAT(Satisfiability)问题的形式如下:

  • 有 \(n\) 个 01 变量 \(x_1,x_2,\cdots,x_n\),另有 \(m\) 个变量取值需要满足的限制。

  • 每个限制是一个 \(k\) 元组 \((x_{p_1},x_{p_2},\cdots,x_{p_k})\),满足 \(x_{p_1}\oplus x_{p_2}\oplus\cdots\oplus x_{p_k}=a\)。其中 \(a\) 为 \(0\) 或 \(1\),\(\oplus\) 是某种二元 bool 运算(如 或运算 \(\vee\)、与运算 \(\wedge\))。

  • 要求构造一种满足所有限制的变量的赋值方案。

当 \(k>2\) 时该问题为 NP 完全的,只能暴力求解。因此一般讨论的是 \(k=2\) 的情况,即 2-SAT 问题。

二、基本思想

Luogu P4782 【模板】2-SAT 问题 为例,建立图论模型。

\(m\) 个限制,每个限制的形式都是 「\(x_i\) 为 真/假 或 \(x_j\) 为 真/假」。

对于变量 \(x_i\),建立两个点 \(i\) 与 \(i+n\),分别表示 \(x_i\) 为真、\(\neg x_i\) 为真。

若 \(x\) 为真,则 \(\neg x\) 为假;若 \(\neg x\) 为假,则 \(x\) 为真。反之亦然。显然 \(x\) 和 \(\neg x\) 是互斥的。即,点 \(i\) 与 \(i+n\) 分别表示 \(x_i\) 为真或假。

对变量关系建有向图。有向边 \(u\to v\) 表示,若 \(u\) 为真,则 \(v\) 一定为真。

具体地,对于每个限制 \((a\vee b)\)(变量 \(a,b\) 至少满足一个),可将其转化为 \(\neg a\rightarrow b\wedge\neg b\rightarrow a\)(\(a\) 为假则 \(b\) 一定为真;\(b\) 为假则 \(a\) 一定为真)。即节点 \(\neg a\) 向节点 \(b\) 连边,从节点 \(\neg b\) 向节点 \(a\) 连边。

考虑节点 \(i\) 与 \(i+n\) 在图中的关系。若它们 互相可达,即在 同一个强连通分量 中,则说明在赋值限制下,它们代表的一对互斥取值会同时被取到。则不存在一组合法的赋值方案。

否则,说明有解,考虑如何构造一组合法解。

首先,对建出的图进行缩点得到一个 DAG。考虑节点 \(i\) 与 \(i+n\) 所在强连通分量的 拓扑关系。若两分量不连通,则 \(x_i\) 取任意值(真或假)。否则只能取属于拓扑序较大的分量的值。因为若取拓扑序较小的值,可以根据逻辑关系推出取另一个值也是同时发生的。

三、具体实现

Luogu P4782 【模板】2-SAT 问题 为例。

  1. 对于每个限制 \((a\vee b)\)(变量 \(a,b\) 至少满足一个),节点 \(\neg a\) 向节点 \(b\) 连边,从节点 \(\neg b\) 向节点 \(a\) 连边。

  2. 用 Tarjan 算法对建出的图缩点。

  3. 对于 \(i\in [1,n]\),若 \(i\) 与 \(i+n\) 在同一个强连通分量中,则不存在一组合法的赋值方案。

  4. 否则,根据 Tarjan 求得的强连通分量的标号为拓扑逆序(Tarjan 算法求强连通分量时使用了栈),即反向的拓扑序 ,可以得到 \(x_i\) 的值(取 \(i\) 与 \(i+n\) 所在强连通分量拓扑序较大的点的值)。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
int n,m,x,a,y,b,cnt,hd[N],to[N<<1],nxt[N<<1],tot,c[N],top,s[N],num,dfn[N],low[N];
void add(int x,int y){
to[++cnt]=y,nxt[cnt]=hd[x],hd[x]=cnt;
}
void tarjan(int x){
dfn[x]=low[x]=++num,s[++top]=x;
for(int i=hd[x];i;i=nxt[i]){
int y=to[i];
if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
else if(!c[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
c[x]=++tot;
while(s[top]!=x) c[s[top--]]=tot;
--top;
}
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld%lld",&x,&a,&y,&b);
if(a&&b) add(x+n,y),add(y+n,x);
if(!a&&b) add(x,y),add(y+n,x+n);
if(a&&!b) add(x+n,y+n),add(y,x);
if(!a&&!b) add(x,y+n),add(y,x+n);
}
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]) puts("IMPOSSIBLE"),exit(0);
puts("POSSIBLE");
for(int i=1;i<=n;i++)
printf("%d%c",c[i]<c[i+n],i==n?'\n':' '); //Tarjan 求得的强连通分量的标号为拓扑逆序,即反向的拓扑序
return 0;
}

「算法笔记」2-SAT 问题的更多相关文章

  1. 「算法笔记」快速数论变换(NTT)

    一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...

  2. 「算法笔记」树形 DP

    一.树形 DP 基础 又是一篇鸽了好久的文章--以下面这道题为例,介绍一下树形 DP 的一般过程. POJ 2342 Anniversary party 题目大意:有一家公司要举行一个聚会,一共有 \ ...

  3. 「算法笔记」Polya 定理

    一.前置概念 接下来的这些定义摘自 置换群 - OI Wiki. 1. 群 若集合 \(s\neq \varnothing\) 和 \(S\) 上的运算 \(\cdot\) 构成的代数结构 \((S, ...

  4. 「算法笔记」状压 DP

    一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...

  5. 「算法笔记」旋转 Treap

    一.引入 随机数据中,BST 一次操作的期望复杂度为 \(\mathcal{O}(\log n)\). 然而,BST 很容易退化,例如在 BST 中一次插入一个有序序列,将会得到一条链,平均每次操作的 ...

  6. 「算法笔记」FHQ-Treap

    右转→https://www.cnblogs.com/mytqwqq/p/15057231.html 下面放个板子 (禁止莱莱白嫖板子) P3369 [模板]普通平衡树 #include<bit ...

  7. 「算法笔记」Min_25 筛

    戳 这里(加了密码).虽然写的可能还算清楚,但还是不公开了吧 QwQ. 真的想看的 私信可能会考虑给密码 qwq.就放个板子: //LOJ 6053 简单的函数 f(p^c)=p xor c #inc ...

  8. 「算法笔记」快速傅里叶变换(FFT)

    一.引入 首先,定义多项式的形式为 \(f(x)=\sum_{i=0}^n a_ix^i\),其中 \(a_i\) 为系数,\(n\) 为次数,这种表示方法称为"系数表示法",一个 ...

  9. 「算法笔记」BSGS 与 exBSGS

    一.离散对数 给定 \(a,b,m\),存在一个 \(x\),使得 \(\displaystyle a^x\equiv b\pmod m\) 则称 \(x\) 为 \(b\) 在模 \(m\) 意义下 ...

随机推荐

  1. 半天做完的数据报表,YonBuilder只要十几分钟,0代码开发

    进入数字化时代,拍脑袋的决策方式显然不靠谱,一切要靠数据说话.与信息化时代相比,数字化时代的企业对数据的应用更广泛.更深入.为了应对激烈的市场竞争,企业经营决策者们对数据的依赖度越来越高,企业各个业务 ...

  2. 巩固javaweb的第三十天

    显示用户输入信息 1 .代码 要想输出用户在上一个页面提交的信息,可以使用下面的代码: ${param.userid} ${param.username} ${param.userpass} ${pa ...

  3. HDFS02 HDFS的Shell操作

    HDFS的Shell操作(开发重点) 目录 HDFS的Shell操作(开发重点) 基本语法 常用命令 准备工作 上传 -moveFromLocal 剪切 -copyFromLocal 拷贝 -put ...

  4. Spark(十六)【SparkStreaming基本使用】

    目录 一. SparkStreaming简介 1. 相关术语 2. SparkStreaming概念 3. SparkStreaming架构 4. 背压机制 二. Dstream入门 1. WordC ...

  5. How exactly does Google AdWords work?

    The key to how Google AdWords works is the Quality Score. Quality Score is generally how well an ad ...

  6. Linux基础命令---mput上传ftp文件

    mput 使用lftp登录ftp服务器之后,可以使用put指令将文件上传到服务器.mput指令可以使用通配符,而put指令则不可以.   1.语法       mput [-c]  [-d] [-a] ...

  7. linux网络相关命令之脚本和centos启动流程

    nice 功用:设置优先权,可以改变程序执行的优先权等级.等级的范围从-19(最高优先级)到20(最低优先级).优先级为操作系统决定cpu分配的参数,优先级越高,所可能获得的 cpu时间越长. 语法: ...

  8. 1.Vuejs-第一个实例

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. synchronized底层浅析(二)

    一张图了解锁升级流程:

  10. 【React】组件书写记录

    时钟组件: 组件形式:数字时钟 https://blog.csdn.net/hahahahahahahaha__1/article/details/80688920 Javascript获取时间方法: ...