「算法笔记」2-SAT 问题
一、定义
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 问题 为例。
对于每个限制 \((a\vee b)\)(变量 \(a,b\) 至少满足一个),节点 \(\neg a\) 向节点 \(b\) 连边,从节点 \(\neg b\) 向节点 \(a\) 连边。
用 Tarjan 算法对建出的图缩点。
对于 \(i\in [1,n]\),若 \(i\) 与 \(i+n\) 在同一个强连通分量中,则不存在一组合法的赋值方案。
否则,根据 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 问题的更多相关文章
- 「算法笔记」快速数论变换(NTT)
一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...
- 「算法笔记」树形 DP
一.树形 DP 基础 又是一篇鸽了好久的文章--以下面这道题为例,介绍一下树形 DP 的一般过程. POJ 2342 Anniversary party 题目大意:有一家公司要举行一个聚会,一共有 \ ...
- 「算法笔记」Polya 定理
一.前置概念 接下来的这些定义摘自 置换群 - OI Wiki. 1. 群 若集合 \(s\neq \varnothing\) 和 \(S\) 上的运算 \(\cdot\) 构成的代数结构 \((S, ...
- 「算法笔记」状压 DP
一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...
- 「算法笔记」旋转 Treap
一.引入 随机数据中,BST 一次操作的期望复杂度为 \(\mathcal{O}(\log n)\). 然而,BST 很容易退化,例如在 BST 中一次插入一个有序序列,将会得到一条链,平均每次操作的 ...
- 「算法笔记」FHQ-Treap
右转→https://www.cnblogs.com/mytqwqq/p/15057231.html 下面放个板子 (禁止莱莱白嫖板子) P3369 [模板]普通平衡树 #include<bit ...
- 「算法笔记」Min_25 筛
戳 这里(加了密码).虽然写的可能还算清楚,但还是不公开了吧 QwQ. 真的想看的 私信可能会考虑给密码 qwq.就放个板子: //LOJ 6053 简单的函数 f(p^c)=p xor c #inc ...
- 「算法笔记」快速傅里叶变换(FFT)
一.引入 首先,定义多项式的形式为 \(f(x)=\sum_{i=0}^n a_ix^i\),其中 \(a_i\) 为系数,\(n\) 为次数,这种表示方法称为"系数表示法",一个 ...
- 「算法笔记」BSGS 与 exBSGS
一.离散对数 给定 \(a,b,m\),存在一个 \(x\),使得 \(\displaystyle a^x\equiv b\pmod m\) 则称 \(x\) 为 \(b\) 在模 \(m\) 意义下 ...
随机推荐
- 学习java的第六天
一.今日收获 1.开始了学习手册第二章的学习 2.了解了java里的常量与变量以及数据类型,与c语言的内容类似 二.今日难题 1.都是基础知识,没有什么难题 三.明日目标 1.继续学习java学习手册 ...
- Typora数学公式输入指导手册
Markdown 公式指导手册 公式大全的链接 https://www.zybuluo.com/codeep/note/163962#mjx-eqn-eqsample 目录 Markdown 公式指导 ...
- acre, across
acre The acre is a unit of land area used in the imperial and US customary systems. It is traditiona ...
- Flink(二)【架构原理,组件,提交流程】
目录 一.运行架构 1.架构 2.组件 二.核心概念 TaskManager . Slots Parallelism(并行度) Task .Subtask Operator Chains(任务链) E ...
- CSS相关,手画三角形,正方形,扇形
三角形 实现一个三角形 <!DOCTYPE html> <html> <head> <title>三角形</title> <style ...
- 【leetcode】1217. Minimum Cost to Move Chips to The Same Position
We have n chips, where the position of the ith chip is position[i]. We need to move all the chips to ...
- 【leetcode】834. Sum of Distances in Tree(图算法)
There is an undirected connected tree with n nodes labeled from 0 to n - 1 and n - 1 edges. You are ...
- Shell学习(六)——条件判断总结
Shell学习(六)--条件判断总结 [1]https://www.cnblogs.com/zhw-626/p/8528001.html [2]https://www.cnblogs.com/yizh ...
- 节省内存的循环banner(一)
循环banner是指scrollview首尾相连,循环播放的效果,使用非常广泛.例如淘宝的广告栏等. 如果是简单的做法可以把所有要显示的图片全部放进一个数组里,创建相同个数的图片视图来显示图片.这样的 ...
- Linux学习 - 网络命令
一.write 1 功能 给指定在线用户发信息,以Ctrl + D保存结束 2 语法 write <用户名> [信息] 二.wall(write all) 1 功能 给所有在线用户发送 ...