2-SAT问题介绍求解 + 模板题P4782
什么是2-SAT问题
sat 即 Satisfiability,意思为可满足,那么2-SAT表示一些布尔变量只能取true或者false,而某两个变量之间的值存在一定的关系(如:只要a为真,b一定为假;如果a为假,b也为假),我们需要在满足所有这样的关系的情况下,求出每个变量的赋值,如果不存在,就是无解。
举个栗子
某一天,PC,YD,HL在讨论两个问题:1)winter的温度是否低于0度,2)bamboo的高度是否大于10m,众所周知,这三位大佬很强大,所以他们对于这两个问题的判断至少有一个是正确的。此时给出三人对这两个问题的判断,求出这两个问题的正确答案;如果不存在正确答案,那么可能是某个人犯糊涂了,因此我们无法得到答案,输出无解。
我们将两个问题符号化为,a:winter的温度低于0度, b :bamboo的高度大于10m,将三人的观点及其符号化表示如下:
1)PC认为:winter的温度低于0度,bamboo的高度大于10m,a∨ b
2)YD认为:wintet的温度不低于0度,bamboo的高度大于10m,¬a∨ b
3)HL认为:winter的温度不低于0度,bamboo的高度不大于10m,¬a∨ ¬b
那么a,b的取值需要满足:(a∨ b) ∧ ( ¬a∨ b) ∧ (¬a∨ ¬b),我们如何求出满足这个式子的a,b的取值这类问题就是2-SAT问题。
将2-SAT问题转为图论问题求解
我们可以将每个变量x的两个状态分别用两个点表示,记编号为i的点表示这个变量取真,i+n的点表示这个变量取假
将两个变量之间的关系用边表示,如:只要a为真,b一定为假,将此命题符号化得到¬a∨¬b,这个式子也可以写成:a → ¬b ∧ b → ¬a,表示:a为真,则b必为假 ,和b为真,则a必为假,这样我们就发现 a 和 ¬b 可以由a推出¬b, 可以由b推出¬a,在图中,我们构建这样的边表示他们的关系:由 a 向 ¬b 建一条单向边,再由 b 向 ¬a建一条单向边,总结以下将命题转化为建图的规律:
1)¬a∨¬b ----> a → ¬b ∧ b → ¬a
2)a∨b ----> ¬a → b ∧ ¬b → a
3)¬a∨b ----> a → b ∧ ¬b → ¬a
4)a∨¬b ----> ¬a → ¬b ∧ b → a
(x→y可以视作由x向y建一条单向边)
然后,我们在这个图中求强连通分量,联想到我们图中边代表的关系:由边的起点可以推出终点,所以同一强连通分量中的点真值一致,所以我们很容易想到如果 a 和 ¬a在同一强连通分量中,这说明a和¬a真值相同,这显然是不正确的,即无解;如果不存在变量x使得x和¬x在同一强连通分量中,就说明有解。
那么有解的情况下,我们如何得到每个变量的值呢?只要x所在强连通分量的拓扑序比¬x所在强连通分量的拓扑序靠后,则x为真,否则为假,而我们用tarjan算法求强连通分量的时候,对于每个强连通分量的标记是逆拓扑序的,所以 node[x] < node[¬x] 时,x取真,否则为假,这样一来,我们对每个变量进行判断并输出对应的值即可
代码区
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip> #define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const ll inf = 2e9 + ;
const ll mod = 1e9 + ;
const int Max = 2e6 + ;
const int Max2 = 3e2 + ; struct Egde
{
int to, next;
} edge[Max]; int n, m;
int head[Max], tot;
int dfn[Max], low[Max],time_clock;
int line[Max],now;
int node[Max],sccCnt; void init()
{
memset(head, -, sizeof(head));
tot = ;
memset(dfn,,sizeof(dfn));
time_clock = ;
now = ;
memset(node,,sizeof(node));
sccCnt = ;
} void add(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
} void tarjan(int u)
{
dfn[u] = low[u] = ++time_clock;
line[++now] = u;
for(int i = head[u] ; i != -; i = edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(!node[v])
{
low[u] = min(low[u],dfn[v]);
}
}
if(dfn[u] == low[u])
{
sccCnt++;
while(line[now] != u)
node[line[now--]] = sccCnt;
node[line[now--]] = sccCnt;
}
} int main()
{
#ifdef LOCAL
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
while (scanf("%d%d", &n, &m) != EOF)
{
init();
for (int i = , a, va, b, vb; i <= m; i++)
{
scanf("%d%d%d%d", &a, &va, &b, &vb);
add(a + n * (va & ), b + n * (vb ^ ));
add(b + n * (vb & ), a + n * (va ^ )); //1~n表示1,n+1,2*n表示0
}
for(int i = ;i <= (n << ) ;i ++)
{
if(!dfn[i])
tarjan(i);
}
bool ok = true;
for(int i = ;i <= n ;i ++)
{
if(node[i] == node[i+n])
{
ok = false;
break;
}
}
if(!ok)
{
printf("IMPOSSIBLE\n");
}
else
{
printf("POSSIBLE\n");
for(int i = ;i < n ;i ++)
{
printf("%d ",node[i] < node[i+n]);
}
printf("%d\n",node[n] < node[n<<]);
}
}
return ;
}
2-SAT问题介绍求解 + 模板题P4782的更多相关文章
- poj3250(单调栈模板题)
题目链接:https://vjudge.net/problem/POJ-3250 题意:求序列中每个点右边第一个>=自身的点的下标. 思路:简单介绍单调栈,主要用来求向左/右第一个小于/大于自身 ...
- bzoj2049-洞穴勘测(动态树lct模板题)
Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...
- [置顶] 小白学习KM算法详细总结--附上模板题hdu2255
KM算法是基于匈牙利算法求最大或最小权值的完备匹配 关于KM不知道看了多久,每次都不能完全理解,今天花了很久的时间做个总结,归纳以及结合别人的总结给出自己的理解,希望自己以后来看能一目了然,也希望对刚 ...
- POJ 1985 Cow Marathon (模板题)(树的直径)
<题目链接> 题目大意: 给定一颗树,求出树的直径. 解题分析:树的直径模板题,以下程序分别用树形DP和两次BFS来求解. 树形DP: #include <cstdio> #i ...
- HDU 4825 Xor Sum (模板题)【01字典树】
<题目链接> 题目大意: 给定n个数,进行m次查找,每次查找输出n个数中与给定数异或结果最大的数. 解题分析: 01字典树模板题,01字典树在求解异或问题上十分高效.利用给定数据的二进制数 ...
- POJ 3264 Balanced Lineup(模板题)【RMQ】
<题目链接> 题目大意: 给定一段序列,进行q次询问,输出每次询问区间的最大值与最小值之差. 解题分析: RMQ模板题,用ST表求解,ST表用了倍增的原理. #include <cs ...
- SPOJ RPLN (模板题)(ST算法)【RMQ】
<题目链接> 题目大意:给你一段序列,进行q次区间查询,每次都输出询问区间内的最小值. 解题分析: RMQ模板题,下面用在线算法——ST算法求解.不懂ST算法的可以看这篇博客 >& ...
- POJ 1330 Nearest Common Ancestors (模板题)【LCA】
<题目链接> 题目大意: 给出一棵树,问任意两个点的最近公共祖先的编号. 解题分析:LCA模板题,下面用的是树上倍增求解. #include <iostream> #inclu ...
- POJ2417 Discrete Logging【BSGS】(模板题)
<题目链接> 题目大意: P是素数,然后分别给你P,B,N三个数,然你求出满足这个式子的L的最小值 : BL== N (mod P). 解题分析: 这题是bsgs算法的模板题. #incl ...
随机推荐
- vue-cli3项目中引入jquery 以及如何引进bootstrap
1.安装jquery npm install jquery --save 2.或则在package.json中指定版本号,然后运行npm install命令 "dependencies&qu ...
- Spring Boot教程(三十五)使用MongoDB数据库(1)
MongoDB简介 MongoDB是一个基于分布式文件存储的数据库,它是一个介于关系数据库和非关系数据库之间的产品,其主要目标是在键/值存储方式(提供了高性能和高度伸缩性)和传统的RDBMS系统(具有 ...
- Linux配置tomcat开机自启
转自百度经验:https://jingyan.baidu.com/article/6525d4b1382f0aac7d2e9421.html 端口也需要保持开机自启详细请看:https://www.c ...
- 2016 ICPC 大连网络赛 部分题解
先讲1007,有m个人,n种石头,将n种石头分给m个人,每两个人之间要么是朋友关系,要么是敌人关系,朋友的话他们必须有一种相同颜色的石头,敌人的话他们必须所有石头的颜色都不相同.另外,一个人可以不拥有 ...
- 撩测试MM神器cypress使用入门
不很久不很久以前 据说某家公司有两位前端,天天撸bug,为啥嘞?只怪测试MM倾人国,轻语哥哥有bug.✧(๑•̀ㅂ•́)و✧ 可是最近两位有点犯愁 Σ(っ °Д °;)っ.测试MM有几次提了紧急bug ...
- JS基础_使用工厂方法创建对象
创建一个对象 var obj={ name:"hhh", age:28, gender:"男", say:function(){ console.log(&qu ...
- 安卓 API 19 低版本设置自带的圆圈效果
在 Android API 19 环境下,RadioButton 消除或者自定义自带的圆圈效果的形式来设置: 自定义自身选择图标 android:button="@drawable/sele ...
- LeetCode 81. 搜索旋转排序数组 II(Search in Rotated Sorted Array II)
题目描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] ). 编写一个函数来判断给定的目标值是否存在 ...
- SVN图标详解
蓝色的加号 : 把这个文件已经添加到版本控制软件内 绿色的对勾 : 客户端和服务器端的代码一致 红色的叹号 : 客户端和服务器端两边的代码不一致 黄色的叹号 : 文件冲突 蓝色的问号 : 这个文件不在 ...
- 在Latex中插入Python代码
这里指的插入是指最终能在生成的pdf中显示高亮的Python代码. 在Latex中插入Python代码,需要一个第三发的宏包pythonhighlight: https://github.com/ol ...