2-SAT 入门

一、前置技能

>  Tarjan求割点

>  高中数学选修2-1 命题与逻辑关系(好像是这个名字)

>  莓了

(本文用&&表示与 || 表示或  !x表示x的否命题 与正常的数学符号不同食用请注意)

二、k-SAT

>  Q: 为什么讲2-SAT之前要先讲k-SAT?

因为k-SAT被证明是NP-完全问题

所以暴搜就不去管它……

>  Q: 所以什么是“SAT”?

由美国大学委员会主办的一场考试

SAT被称为布尔方程的可满足性问题

就是判断一组逻辑关系同时满足的正确性……

比如a&&(!a) 就不满足

再比如a&&b||c&&!d&&(.....)

我们给一种特殊的布尔方程起一个名字叫 合取范式 

就是形如(a || !b || c) && (d || e) && (...)

其中每个bool变量叫 文字 打了括号的部分就叫 子句

如果每个子句大小不超过k那么就叫 k-SAT

同理 如果合取范式的每个子句中的文字个数都不超过两个,那么对应的 SAT 问题又称为 2-SAT 问题。

三、解法

考虑一个二分图 左边表示bool变量值为0(伪) 右边表示值为1(真)

如果满足一个命题必然能推出另一个命题 那么就在这两个命题之间连一条边

考虑两个bool变量之间的关系无非是 && 和 ||

由于我们是2-SAT 所以最多出现a || b两个变量

那么转化为蕴含(推出)关系

|| 运算的定义

!a => b 且 !b => a

然后是 &&

!a => a 且 !b => b  

 >  Q: 这……bool变量还能同时满足真和伪?

当然不是 这里的推出 可以理解为如果选择了a为伪,那么最后会推出a为真

所以我们最后选择的时候不能选择a为伪 只能选择a为真 达到了我们的目的

 >  Q: 好吧……可是,为什么要这么建图?

我们考虑bool运算的性质

命题成立则它的逆否命题成立 这个性质又叫 对称性

同时考虑推出的 传递性  ,如果b是由某一个变量推知为真 和它为真的效果是一样的 都能推知下一步的变量

所以拆完真伪最终在一个强连通分量里的点的bool值相同(注意并不是变量的bool值相同)

同时上一个Q表示建图可以处理单个变量定值的问题

 >  Q:嗯……如何求出最终答案呢?

我们对于最后建好的图跑一个Tarjan缩点 如果变量的真值和伪值在同一个强连通分量里(即既不能为真也不能为伪)那么就无解

否则 每个点的答案应该是真值和伪值中拓扑序靠后的那个(同第一个Q)

(所以要缩点)

然后由于我们跑tarjan的时候 就是拓扑序靠后的强连通分量编号小

所以就不用建新图辣

四、代码

Code:(luogu P4782)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 #define itn int
 6 #define ms(a,b) memset(a,b,sizeof a)
 7 #define rep(i,a,n) for(int i = a;i <= n;i++)
 8 #define per(i,n,a) for(int i = n;i >= a;i--)
 9 using namespace std;
10 typedef long long ll;
11 ll read() {
12     ll as = 0,fu = 1;
13     char c = getchar();
14     while(c < ‘0‘ || c > ‘9‘) {
15         if(c == ‘-‘) fu = -1;
16         c = getchar();
17     }
18     while(c >= ‘0‘ && c <= ‘9‘) {
19         as = as * 10 + c - ‘0‘;
20         c = getchar();
21     }
22     return as * fu;
23 }
24 const int N = 2000005;
25 //head
26 int n,m;
27 int head[N],nxt[N<<1],mo[N<<1],cnt = -1;
28 bool flag[N];
29 void add(itn x,int y) {
30     mo[++cnt] = y;
31     nxt[cnt] = head[x];
32     head[x] = cnt;
33 }
34 int rev(int x) {return x>n?x-n:x+n;}
35
36 int dfn[N],low[N],col[N],stk[N];
37 int scc,idx,top;
38 bool ins[N];
39 void tarjan(int x) {
40     dfn[x] = low[x] = ++idx;
41     stk[++top] = x,ins[x] = 1;
42     for(int i = head[x];i;i = nxt[i]) {
43     int sn = mo[i];
44     if(!dfn[sn]) {
45         tarjan(sn);
46         low[x] = min(low[x],low[sn]);
47     } else if(ins[sn]) low[x] = min(low[x],dfn[sn]);
48     }
49     if(dfn[x] == low[x]) {
50     int t = -1;
51     scc++;
52     while(t!=x) {
53         t = stk[top--];
54         col[t] = scc;
55         ins[t] = 0;
56     }
57     }
58 }
59
60 int main() {
61     n = read();
62     m = read();
63     rep(i,1,m) {
64     int x = read();x += read()*n;
65     int y = read();y += read()*n;
66     add(rev(x),y),add(rev(y),x);
67     }
68     rep(i,1,n<<1) if(!dfn[i]) tarjan(i);
69     bool flag = 1;
70     rep(i,1,n) if(col[i] == col[n+i]) flag = 0;
71     if(!flag) puts("IMPOSSIBLE");
72     else {
73     puts("POSSIBLE");
74     rep(i,1,n) printf("%d ",col[i] > col[i+n]);
75     puts("");
76     }
77     return 0;
78 }

注意:

1.跑的时候依然像Tarjan一样考虑多个联通块

2.本题全部是或的关系 注意反向建图

3.最后比较的时候每个人写法不一样要考虑大于还是小于

例题详见Practice 1

By prophetB

原文地址:https://www.cnblogs.com/yuyanjiaB/p/9762501.html

时间: 2024-10-19 06:55:03

2-SAT 入门的相关文章

SAT求解器快速入门

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

spring boot学习总结(一)-- 基础入门 Hello,spring boot!

写在最前 SpringBoot是伴随着Spring4.0诞生的: 从字面理解,Boot是引导的意思,因此SpringBoot帮助开发者快速搭建Spring框架: SpringBoot帮助开发者快速启动一个Web容器: SpringBoot继承了原有Spring框架的优秀基因: SpringBoot简化了使用Spring的过程.  Spring Boot解决哪些问题??? Spring Boot使编码变简单,使配置变简单,使部署变简单,使监控变简单 下面正式开始!!!快速入门!!! 1.构建Mav

Yii2框架RESTful API教程(一) - 快速入门

前不久做一个项目,是用Yii2框架写一套RESTful风格的API,就去查了下<Yii 2.0 权威指南 >,发现上面写得比较简略.所以就在这里写一篇教程贴,希望帮助刚接触Yii2框架RESTful的小伙伴快速入门. 一.目录结构 实现一个简单地RESTful API只需用到三个文件.目录如下: frontend ├─ config │ └ main.php ├─ controllers │ └ BookController.php └─ models └ Book.php 二.配置URL规则

bash入门教程

shell的种类: sh  - Bourne shell csh or tcsh - C shell korn - Korn shell bash - GNU Bourne-Again shell 1.最简单的列子 例子 #!/bin/bash # This is a very simple example echo Hello World echo Hello World 解释: 在 BASH 中 第一行的 "#!" 及后面的 "/bin/bash" 就表明该文件

python数据分析入门学习笔记儿

学习利用python进行数据分析的笔记儿&下星期二内部交流会要讲的内容,一并分享给大家.博主粗心大意,有什么不对的地方欢迎指正~还有许多尚待完善的地方,待我一边学习一边完善~ 前言:各种和数据分析相关python库的介绍(前言1~4摘抄自<利用python进行数据分析>) 1.Numpy: Numpy是python科学计算的基础包,它提供以下功能(不限于此): (1)快速高效的多维数组对象naarray (2)用于对数组执行元素级计算以及直接对数组执行数学运算的函数 (3)用于读写硬盘

Linux 入门学习-LINUX命令行描述及常用命令

Linux基础入门之(常用命令) 1.命令行构成 命令提示符一般有GUI.GLI两种接口 1.1命令行组成:命令提示符.prompt.bash(使用的shell) 1.1.1  提示符格式默认为系统变量设置 使用echo命令输出PS1系统本地变量 [[email protected] testdir]# echo $PS1 [\[email protected]\h \W]\$ 1.1.2  用户登录提示符: $:表示普通用户 #:表示系统用户 pwd 显示当前用户所在目录 [[email pr

161103、Spring Boot 入门

Spring Boot 入门 spring Boot是Spring社区较新的一个项目.该项目的目的是帮助开发者更容易的创建基于Spring的应用程序和服务,让更多人的人更快的对Spring进行入门体验,让Java开发也能够实现Ruby on Rails那样的生产效率.为Spring生态系统提供了一种固定的.约定优于配置风格的框架. Spring Boot具有如下特性: 为基于Spring的开发提供更快的入门体验 开箱即用,没有代码生成,也无需XML配置.同时也可以修改默认值来满足特定的需求. 提

Quartz 入门详解

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的日程序表.Jobs可以做成标准的Java组件或 EJBs.官方网站:http://www.opensymphony.com/quartz 相关Jar:   quartz-all-1.6.0.jar   jta.jar   commons-logging-1.1.jar

30分钟Git命令“从入门到放弃”

git 现在的火爆程度非同一般,它被广泛地用在大型开源项目中,但是初学者非常容易"从入门到放弃",各种命令各种参数,天哪,宝宝要吓哭了.实际上新手并不需要了解所有命令的用途,学习是需要一个循序渐进的过程,你可以从强大的命令开始.这个是给新手或熟悉图形工具的老鸟们看的教程,"从入门到放弃" 一.基本了解 git命令是一些命令行工具的集合,它可以用来跟踪,记录文件的变动.比如你可以进行保存,比对,分析,合并等等.这个过程被称之为版本控制.已经有一系列的版本控制系统,比如

Git 入门 ---- Git 常用命令

本文作为  Git 入门 ---- Git 与 SVN 区别 的续篇,继续介绍 Git 的入门知识 四. Git 安装 OS X 版本: Mac 一般自带不需要安装 Windos 版本: https://git-for-windows.github.io/ Linux 版本: Linux 一般也是自带无需安装 五. 创建新仓库 创建新文件夹,cd 到文件夹,执行命令: git init  用来创建新 git 仓库 六. 提交文件进仓库 在新建好 git 仓库的文件夹里加入 readme.txt