2-SAT到图论

\(k-SAT\) 是 k-适应性问题(Satisfiability)的简称。

\(k-SAT\) 问题(除 \(k = 2\))已被证明为是 \(NP\) 完全问题, 而对于 \(k = 2\) 的 \(2-SAT\) 问题, 我们可以用图论求解。

\(k-SAT\) 与 \(2-SAT\) 最大的不同在与这个\(2\) 废话, 就因为这个 “2”, 我们可以由假设的第一个限制条件推知第二个限制条件

举个栗子: 限制条件为 \(a\) ^ $b = 1 $ (对于 \(a\) )有

\[ a\ xor\ b = 1 \Rightarrow \left\{
\begin{aligned}
a = 1 \Rightarrow b = 0 \\
a = 0 \Rightarrow b = 1
\end{aligned}
\right.
\]

依据限制条件, 我们可以搞一个通式有助后边理解: \(a = x\) \(b = y\) (a、 b、 x 已知且 \(x, y \in {0, 1}\) )

于是, 我们把问题转换到图中:引入一条有向边, 起点为通式则的左边, 终点为则右边, 这条边的意义为 必须选中, 即选择了起点就必须选择终点。

以上文的限制条件为例,我们将 x 变量为假设为 x (\(x << 1\)), x 变量为真设为 x'(\(x << 1 | 1\)) , 可以作如下转换(对于 \(a\) ):

\[ a\ xor\ b = 1 \Rightarrow \left\{
\begin{aligned}
a'\ \rightarrow\ b \\
a\ \rightarrow\ b'
\end{aligned}
\right.
\]

其代表的含义为: 选择了 a 为真则一定选择 b 为假, 选了 a 为假则一定选择 b 为真

这只是对 \(a\) 进行讨论, 事实上对于异或这一条件我们需要连 \(4\) 条边(上面两条和对于 \(b\) 的两条)

可以联想到: 在此图中沿着一条路走下去其实就是逻辑推断的过程

合法性的判断

这样我们就得到了一个图。 如何快速的判断是否能满足所有限制条件呢? 我们需要在图上做文章了。

不合法显然意味着矛盾, 什么时候会产生矛盾呢?

一个人不可能既是男的又是女的。 花是妹子, 花是男的, 这显然就矛盾了吗

我们假设 \(x\) 为假, 经过一系列推断, 我们推出了 \(x\) 为真, 这显然矛盾, 问题无解

上文提到过, 推断过程其实就是走有向边遍历的过程, 那么若是存在一个环, 同时包含了 \(x\) 和 \(x'\), 那么问题无解。

更确切的说: 当一个变量的两对立面(即 \(x\) 和 \(x'\))同时存在于一个强联通分量中时, 问题无解。

所以我们跑一次 \(Tarjan\) 判断每一元素的两对立面是否在同一强联通分量即可判断是否合法

方案输出

先导知识: 你真的了解 Tarjan 吗

你可能知道: \(Tarjan\) 可以求强联通分量, 并对在同一个强联通分量中的点染色

可你真的知道染色的顺序有什么流弊的东西吗

反向拓扑序? 拜托, 别搞那些花里胡哨的

因为 \(Tarjan\) 是利用 \(dfs\) 实现的, 说到底用的是, 先进后出, 所以对于两个点 \(u,v\) 若是两个点没有被 \(Tarjan\) 过, 且能从 \(u\) 点 到达 \(v\) 点, 那么 \(col[v] <= col[u]\) (其中 \(=\) 是两点处于一个强联通分量的时候), 即 \(v\) 比 \(u\) 先完成染色(其实就是个逆拓扑序啦)

而对于两个点, 他们无法互相通达, 那么先 \(Tarjan\) 哪一个, 那一个点的颜色编号较小

优先级

在 \(2-SAT\) 问题中, 我们用 \(Tarjan\) 不仅是求出了强联通分量, 他还进行了一个遍历整个图的过程, 也就是说, 我们利用 \(Tarjan\) 完成了逻辑推理

因为遍历有先后, 所以所对应的逻辑推理也有一个优先级(这点很重要): 试想现在有一个点 \(X\), 我们从 \(X\) 出发进行 \(Tarjan\) ,他的所有子节点(\(x_{1},x_{2}...,x_{n}\))的颜色编号小于等于此节点的颜色编号(即 \(col[X] >= col[x_{1},x_{2}...,x_{n}]\)), 我们可以认为这些子节点是由这一个点逻辑推理出来的; 好的现在我们遍历下一个点 \(Y\), 他的所有子节点(\(y_{1},y_{2}...,y_{m}\))颜色编号小于 \(col[Y]\), 所以我们可以得到一个式子:

\[col[x_{1},x_{2}...,x_{n}] <= col[X] <= col[y_{1},y_{2}...,y_{m}] <= col[Y]
\]

从中间的 \(<=\) 号处切开, 左右分类, 可以很清晰的看出他们的优先级关系, 所以我们得到结论: 颜色标号越小, 优先级越高

所以输出方案时(当然已经判断合法后)选取真和假这两个对立面中颜色编号较小的点, 他的优先级高, 我们选取他即可

几种模型

逻辑运算无非 \(or, and, xor\) 这三种, 所以模型并不是很多(依然 \(x\) 代表假, \(x'\) 代表真)

\(a\ or\ b = 0\)

即 \(a = b = 0\), 优先级 \(0 >1\)

\(a' \rightarrow a\)

\(b' \rightarrow b\)

\(a\ or\ b = 1\)

即 两个不能同时为 \(0 \Rightarrow\) 一个为 \(0\) 则另一个为 \(1\)

\(a \rightarrow b'\)

\(b \rightarrow a'\)

\(a\ and\ b = 0\)

即 两个不能同时为 \(1 \Rightarrow\) 一个为 \(1\) 则另一个为 \(0\)

\(a' \rightarrow b\)

\(b' \rightarrow a\)

\(a\ and\ b = 1\)

即 \(a = b = 1\), 优先级 \(1 > 0\)

\(a \rightarrow a'\)

\(b \rightarrow b'\)

\(a\ xor\ b = 0\)

即 两个变量相同

\(a \rightarrow b\)

\(a' \rightarrow b'\)

\(b \rightarrow a\)

\(b' \rightarrow a'\)

\(a\ xor\ b = 1\)

即 两个变量不同

\(a \rightarrow b'\)

\(a' \rightarrow b\)

\(b \rightarrow a'\)

\(b' \rightarrow a\)

P4782 【模板】2-SAT 问题

题目背景

2-SAT 问题 模板

题目描述

有n个布尔变量 x_1x ~ x_n,另有m个需要满足的条件,每个条件的形式都是“ x_i为true/false或 x_j为true/false”。比如“ x_1为真或 x_3为假”、“ x_7为假或 x_2为假”。2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。

输入输出格式

输入格式:

第一行两个整数n和m,意义如题面所述。

接下来m行每行4个整数 i a j b,表示“ x_i为a或 x_j为b”(a,b∈{0,1})

输出格式:

如无解,输出“IMPOSSIBLE”(不带引号); 否则输出"POSSIBLE"(不带引号),下 一行n个整数 x_1 ~ x_n( x_i∈{0,1}),表示构造出的解。


蛮裸的, 主要靠输出方案, 搞清楚 优先级即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 2000019,INF = 1e9 + 19;
int head[maxn],nume = 1;
struct Node{
int v,dis,nxt;
}E[maxn << 2];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int num, nr;
int DFN[maxn], LOW[maxn], INDEX;
int S[maxn], top;
bool ins[maxn];
int col[maxn], numc;
void Tarjan(int u){
DFN[u] = LOW[u] = ++INDEX;
S[++top] = u;ins[u] = 1;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(!DFN[v])Tarjan(v), LOW[u] = min(LOW[u], LOW[v]);
else if(ins[v])LOW[u] = min(LOW[u], DFN[v]);
}
if(DFN[u] == LOW[u]){
numc++;
while(S[top + 1] != u){
col[S[top]] = numc;
ins[S[top--]] = 0;
}
}
}
int main(){
num = RD();nr = RD();
for(int i = 1;i <= nr;i++){
int a = RD(), x = RD(), b = RD(), y = RD();
//<< 1 | 0 -> 0 , << 1 | 1 -> 1
add(a << 1 | (x ^ 1), b << 1 | y, 1);
add(b << 1 | (y ^ 1), a << 1 | x, 1);
}
for(int i = 2;i <= (num << 1 | 1);i++)if(!DFN[i])Tarjan(i);
for(int i = 1;i <= num;i++){
if(col[i << 1] == col[i << 1 | 1]){
puts("IMPOSSIBLE");
return 0;
}
}
puts("POSSIBLE");
for(int i = 1;i <= num;i++){
if(col[i << 1] < col[i << 1 | 1])printf("0 ");
else printf("1 ");
}
puts("");
return 0;
}

P4782 【模板】2-SAT 问题 && 2-SAT问题的更多相关文章

  1. [洛谷P4782] [模板] 2-SAT 问题

    NOIp后第一篇题解. NOIp我考的很凉啊...... 题目传送门 之前讲过怎么判断2-SAT是否存在解. 至于如何构造一组解: 我们想到对tarjan缩点后的图进行拓扑排序. 那么对于代表0状态的 ...

  2. 浅谈2-SAT

    引入: 相信大家都了解过差分约束系统.差分约束系统的大体意思就是给出一些有某种关系的变量,问你是否有某种赋值使得这些关系全部成立 其实\(2-SAT\)的题目描述和这个很像(虽然解法不一样) 那么\( ...

  3. 《Linux命令行与shell脚本编程大全》第二十章 正则表达式

    20.1 什么是正则表达式 20.1.1 定义 正则表达式是你所定义的模式模板.linux工具可以用它来过滤文本. 正则表达式利用通配符来描述数据流中第一个或多个字符. 正则表达式模式含有文本或特殊字 ...

  4. DirectX11 With Windows SDK--11 混合状态与光栅化状态

    前言 虽然这一部分的内容主要偏向于混合(Blending),但这里还需提及一下,关于渲染管线可以绑定的状态主要有如下四种: 光栅化状态(光栅化阶段) 采样器状态(像素着色阶段) 混合状态(输出合并阶段 ...

  5. MySQL-MHA集群部署(binlog复制)

    MHA的理论知识网上有很多教程,这里不会说明:仅推荐博客链接! MHA的理论说明:http://www.ywnds.com/?p=8094 MHA的安装包需要在google上面下载,或者就是csdn上 ...

  6. DirectX11 With Windows SDK--11 混合状态

    原文:DirectX11 With Windows SDK--11 混合状态 前言 这一章会着重讲述混合状态,在下一章则会讲述深度/模板状态 DirectX11 With Windows SDK完整目 ...

  7. Enter password for default keyring to unlock

    file /home/ok/.gnome2/keyrings/login.keyring /home/ok/.gnome2/keyrings/login.keyring: GNOME keyring, ...

  8. 原来还有这样的记词方法_Java版记不规则动词_博主推荐

    昨天在看一本英语书的不规则动词的时候,突然产生的灵感:就是想把这样记单词简单方式,用程序代码实现,然后,使用户可以与之进行交互 这样,在用户背不规则动词的时候就会轻松把它给记住.基于这一点,于是我就思 ...

  9. FTP常用故障代码注解

    FTP错误列表 出处:http://bbs.enet.com.cn/UserControl?act=13&threadID 作者: |秒杀』| 详细的FTP错误列表 Restart marke ...

  10. python中数据的变量和字符串的常用使用方法

    1.查看变量类型: a=2 print(a,type(a)) print的用法:在print后面跟多个输出,可以用逗号分隔. 回收变量名,如把a存储不同的数据,你不需要删除原有变量就可以直接赋值 2. ...

随机推荐

  1. 【Alpha】阶段第九次Scrum Meeting

    [Alpha]阶段第九次Scrum Meeting 工作情况 团队成员 今日已完成任务 明日待完成任务 刘峻辰 编写获得所有学院接口 登出接口 赵智源 编写alpha版后测试点测试用例 编写脚本实现测 ...

  2. JAVA第一次实验 ——凯撒密码

    课程:Java程序设计 班级:1352 姓名:黄伟业 学号:20135215 成绩:            指导教师:娄嘉鹏  实验日期:2015.4.15 实验密级: 预习程度:  实验时间:19: ...

  3. 读书笔记之java编程思想2

    今天将第一章余下的部分读完了,余下部分讲解了java单继承的特点,单继承保证了所有的子类都有一个基类,这使得java所实现的垃圾回收器的实现变得简单了很多,单继承保证了所有的对象都具有一些功能,使得参 ...

  4. 读书笔记 之java编程思想

    本阶段我正在读java的编程思想这本书,这本书只是刚读了第一章的一部分,有些有些要记得所以记录下来, 我认为要记得有就是复用这样可以对对象进行增强,将一个类作为下一个类中基本类型,这样达到的服用的目的 ...

  5. WebGL学习笔记七点一

    第六章讲的是一些GL的一些语法,前面已经涉及,学习时直接跳过,来看第七章,第七章是真正意义的三维立体的出现,其实图形绘制方法是差不多的,就是Z坐标此时不再为0,所以很容易能构造出一些立体图形,但是立体 ...

  6. Scapy 网段中ping扫描

    安装scapy pip3 install scapy-python3 交互式ip包构造 #scapy >>> ping = sr(IP(dst='202.100.1.1')/ICMP ...

  7. 第一个Sprint冲刺成果

    组长:李咏江,组员:叶煜稳,谢洪跃,周伟雄 进程:第一个算法功能完成

  8. 福州大学软工1816 K 班助教总结

    春节时期总有各种诱惑因素(例如路人超能第二季),拖到现在才发布十分抱歉orz. 一.感谢 首先对柯老师和软工助教指导团队这一学期以来的支持和指导表示感谢.第一次做助教,有时候会提出一些不大成熟的想法, ...

  9. 个人作业4——alpha阶段个人总结(201521123003 董美凤)

    一.个人总结 在alpha 结束之后, 每位同学写一篇个人博客, 总结自己的alpha 过程: 请用自我评价表:http://www.cnblogs.com/xinz/p/3852177.html 有 ...

  10. APP分析----饿了么

    产品      饿了么 选择原因:有了外卖就可以轻松拥有一个不用出门也饿不着的爽歪歪周末. 第一部分 调研, 评测 下载软件并使用起来,描述最简单直观的个人第一次上手体验. 主界面: 第一次上手是大一 ...