2-SAT 问题:

有 n 个变量,每一个变量都是 bool 类型的,除了这 n 个变量以外,我们还有 m 个关系表达式,关系表达式差不多是这样的:

x1 & x2 = false(注意每个表达式只会有两个变量)

问给出 m 个关系表达式后,能否给这 n 个变量找出一个赋值的方法,使得满足所有的表达式;

建边方式:

核心问题只需要考虑有没有解;

对于每个变量都只有两种取值:0 / 1,那么对于每个点,我们把每个变量拆成 true 点和 false 点;

假如我们有一个关系:x1 & x2 = false;

说明 x1 和 x2 中一定有一个为 false;

那么我们可以从 x1 的 true 连向 x2 的 false,从 x2 的 true 连向 x1 的 false;

要注意只有关系明确的时候才能建边;

解释一下为什么这么连边:

如果 x1 的值为 true ,那么如果我们 x2 的值再为 true 的话,就不满足 “ x1 & x2 = false ” 这个式子了,所以如果 x1 为 true 的话是能明确推出 x2 为 false 的;

我们可以从 x2 的 flase 向 x1 的 true 连边吗?这样也能满足关系式;

显然不能,因为如果 x2 为 false 的话,x为 true 或 false 都是可以的,这不是明确的关系,我们不能建边;

所以一般的建边方式为:

若 “ xa = p 或 xb = q ” 中至少有一个满足,那么我们建两条有向边:

xa (¬p) -> xb (q)

xb (¬q) -> xa (p)

可以简单总结为:其中一个不成立则另一个一定成立(这是明确的关系);

如果一个变量必须等于 true,那么我们从这个点的 false 连向这个点的 true,表示我们从 false 走也会走到 true,也就是说只能等于 true;

至于建边的时候怎么给点编号嘛,自定义喽,不过我建议大家这样编号(下面的代码都是这么编号的):

这样的话对于一个变量 k,编号为 k 的点代表了这个变量的 false 点,编号为 k+n 的点代表了这个变量的 true 点;

判解方式:

这么判断是否有解?

无解的情况:某一个变量的 false 能走到 true,从 true 也能走到 false,也就是说,某一个变量的两个取值在同一个强连通分量内的话,就说明无解。

求强联通分量的话常用的方法是 Tarjan 。

否则就是有解的情况,然后它一般让你输出一个给所有变量赋值的方法,使其满足所有的关系式;

那么怎么给变量赋值呢?我们来看一个图:

我们发现,从 x1 的 false 出发会走到 x1 的 true ,也就是说 x1 现在只能等于 true 了;同理从 x2 的 true 出发能走到 x的 false,说明 x2 只能等于 false;

解释一下吧:

如果我们不将 x1 赋值为 true,而是赋值为 false,那会发生什么呢?

由 x1 的 false 是能明确推出 x2 是为 true 的,但是又有 x2 的 true 能明确推出 x1 为 true,这与刚刚我们将 x1 赋值为 false 是相矛盾的;(你也可以这么理解:假如你在解方程,实在是解不出来了,你就来了个特殊值法,将 x 代入 1 试试看,结果解出来等式不成立,就说明 x ≠ 1);

有了这个性质,就说明在有解的情况下,一个变量的两个取值是有前后推导关系的,也就是一个取值直接或间接的指向了另一个取值;

我们所要选的就是被指向的这个取值,不然会产生像上例那样的矛盾;

在拓扑序上的表现为:我们要在两种取值中选择拓扑序较大的那个值;

所以我们接下来要:缩点 + 拓扑 + 染色

其实我们在 Tarjan 的时候就已经求出了强联通分量的拓扑序了,只不过是反序;

(以我的理解是拓扑序越大的点在一棵树上是越靠近叶节点的,然后越靠近叶节点的那些节点在 Tarjan 的时候是越早被缩点的,所以拓扑序越大的点其所在强联通分量编号越小

那么我们只要取两个取值中强联通分量编号较小的所对应的值就可以了;(这是保证不会错的,因为有时候两个值取哪个都行)

上代码(P4782 【模板】2-SAT 问题):

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
long long read()
{
char ch=getchar();
long long a=,x=;
while(ch<''||ch>'')
{
if(ch=='-') x=-x;
ch=getchar();
}
while(ch>=''&&ch<='')
{
a=(a<<)+(a<<)+(ch-'');
ch=getchar();
}
return a*x;
}
const int N=4e6+;
int n,m,a,b,x,y,tim,top,edge_sum,scc_sum;
int dfn[N],low[N],st[N],vis[N],scc[N],head[N];
struct node
{
int to,next;
}A[N];
void add(int from,int to)
{
edge_sum++;
A[edge_sum].next=head[from];
A[edge_sum].to=to;
head[from]=edge_sum;
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++tim;
st[++top]=u;
vis[u]=;
for(int i=head[u];i;i=A[i].next)
{
int v=A[i].to;
if(v==fa) continue;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(vis[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
scc_sum++;
while(st[top]!=u)
{
scc[st[top]]=scc_sum;
vis[st[top]]=;
top--;
}
scc[st[top]]=scc_sum;
vis[st[top]]=;
top--;
}
}
int main()
{
n=read();m=read();
for(int i=;i<=m;i++)
{
a=read();x=read(); //第a个数为x或第b个数为y
b=read();y=read();
if(x==&&y==) //"如果第a个数为0或第b个数为0"至少满足其一
{
add(a+n,b); //a=1则b=0
add(b+n,a); //b=1则a=0
}
if(x==&&y==) //"如果第a个数为0或第b个数为1"至少满足其一
{
add(a+n,b+n); //a=1则b=1
add(b,a); //b=0则a=0
}
if(x==&&y==) //"如果第a个数为1或第b个数为0"至少满足其一
{
add(a,b); //a=0则b=0
add(b+n,a+n); //b=1则a=1
}
if(x==&&y==) //"如果第a个数为1或第b个数为1"至少满足其一
{
add(a,b+n); //a=0则b=1
add(b,a+n); //b=0则a=1
}
}
for(int i=;i<=*n;i++) //对每个变量的每种取值进行tarjan
{
if(!dfn[i]) tarjan(i,);
}
for(int i=;i<=n;i++) //判断无解的情况
{
if(scc[i]==scc[i+n]) //同一变量的两种取值在同一强联通分量里,说明无解
{
printf("IMPOSSIBLE\n");
return ;
}
}
printf("POSSIBLE\n"); //否则就是有解
for(int i=;i<=n;i++)
{
if(scc[i]>scc[i+n]) printf("1 "); //强联通分量编号越小 -> 拓扑序越大 -> 越优
else printf("0 ");
}
return ;
}

还有一个几乎和模板题一样的水题,双倍经验,双倍欢乐qwq!

P4171 [JSOI2010]满汉全席

2-SAT 知识小结的更多相关文章

  1. Android app开发知识小结

    Android知识小结 这是一个知识的总结,所以没有详解的讲解. 一.分辨率Android中dp长度.sp字体使用.px像素.in英寸.pt英寸1/72.mm毫米 了解dp首先要知道density,d ...

  2. C/C++ 位域知识小结

    C/C++ 位域知识小结 几篇较全面的位域相关的文章: http://www.uplook.cn/blog/9/93362/ C/C++位域(Bit-fields)之我见 C中的位域与大小端问题 内存 ...

  3. JAVA 变量 数据类型 运算符 知识小结

    ---------------------------------------------------> JAVA 变量 数据类型 运算符 知识小结 <------------------ ...

  4. html5-基本知识小结及补充

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  5. HTTPS知识小结

    HTTPS知识小结 背景1:TCP握手 internet上的两台机器A,B要建立起HTTP连接了,在这之前要先建立TCP连接,情景大概是这样子的: A:你好,我跟你建立一个TCP好吗? B:好啊. A ...

  6. shell 环境变量的知识小结

    环境变量的知识小结:·变量名通常要大写.·变量可以在自身的Shell及子Shell中使用.·常用export来定义环境变量.·执行env默认可以显示所有的环境变量名称及对应的值.·输出时用“$变量名” ...

  7. 180531-Spring中JavaConfig知识小结

    原文链接:Spring中JavaConfig知识小结/ Sring中JavaConfig使用姿势 去掉xml的配置方式,改成用Java来配置,最常见的就是将xml中的 bean定义, scanner包 ...

  8. javascript之正则表达式基础知识小结

    javascript之正则表达式基础知识小结,对于学习正则表达式的朋友是个不错的基础入门资料.   元字符 ^ $ . * + ? = ! : | \ / ( ) [ ] { } 在使用这些符号时需要 ...

  9. PS修图知识小结

    PS修图知识小结   [1]人去除红眼. 1.用红眼工具,框选红眼不分. 2.用椭圆选区工具,选择红眼部分 3..创建调整图层,亮度.对比度.加大对比度. 4.选择红眼部分.创建调整图层,可选颜色,减 ...

  10. python基础之八:知识小结及补充

    一.python2 与python3 的区别 1.在2中print后可带扣号,也可不带,3中必须带,否则报错! #print 'hello python2' print('hello python3' ...

随机推荐

  1. C# VS启动调试项目允许外网调试(微信开发)

    转发链接:https://blog.csdn.net/sinat_23050697/article/details/62889693 主要效果是本机调试网站,将网站发布到某域名(如m16758r728 ...

  2. 如何把前端用ajax发过来的图片传到node上,并且用node保存在oss图片服务器上?

    一:只上传一张图片 1.1:node需要安装的插件,先安好 npm install ali-oss uuid co --save A.ali-oss 用途:aliyun OSS(Object Stor ...

  3. 行内块inline-block元素之间出现空白间隙原因及解决办法

    首先,来看下具体的问题,下面是用inline-block布局实现的两边固定宽度,中间自适应的html代码: 1 2 3 4 5 6 7 8 9 <section class="layo ...

  4. Selenium文件上传

    转自:https://www.cnblogs.com/miaojjblog/p/9679915.html Web上本地上传图片,弹出的框Selenium是无法识别的,也就是说,selenium本身没有 ...

  5. oracle批量操作

    https://stackoverflow.com/questions/39576/best-way-to-do-multi-row-insert-in-oracle 1 批量insert 方式一: ...

  6. 使用IDEA将springboot框架导入的两种方法

    第一种新建Maven,导入springboot所依赖的jar包   1.新建一个maven项目,下一步命名,保存文件地址,点击完成         2.进去springboot下载(点击进入),复制p ...

  7. css详解1

    1.css的三种引入方式: 1.1.行内引入 <div style="color:red;">魔降风云变</div> <html> <he ...

  8. 个人第5次作业:Alpha2项目的测试

    这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 名字 易林 学号 201731062134 团队名称 <巧克力王子和六个小矮人> 第一 ...

  9. vbs读取TXT每一行并赋值到变量a

    vbs代码: Dim fso,f,a Set fso = CreateObject("Scripting.FileSystemObject") Set f=fso.OpenText ...

  10. test20190803 夏令营NOIP训练19

    60+100+0=160 贪婪大陆 面对蚂蚁们的疯狂进攻,小FF的Tower defence宣告失败--人类被蚂蚁们逼到了Greed Island上的一个海湾.现在,小FF的后方是一望无际的大海, 前 ...