『笔记』2-SAT
前置
\(SAT\) 是适定性( \(Satisfiability\) )问题的简称。一般形式为 \(k \ -\) 适定性问题,简称 \(k-SAT\) 。而当 \(k>2\) 时该问题为 \(NP\) 完全的。所以我们只研究 \(k=2\) 的情况。
定义
\(2-SAT\) ,简单的说就是给出 \(n\) 个集合,每个集合有两个元素,已知若干个 \(<a,b>\) ,表示 \(a\) 与 \(b\) 矛盾(其中 \(a\) 与 \(b\) 属于不同的集合)。然后从每个集合选择一个元素,判断能否一共选 \(n\) 个两两不矛盾的元素。显然可能有多种选择方案,一般题中只需要求出一种即可。
有学长者云:
有 \(n\) 个 \(01\) 变量 \(x_1∼x_n\),另有 \(m\) 个变量取值需要满足的限制。
每个限制是一个 $$ 元组 \((x_{p1},x_{p2},\dots,x_{pk})\) ,满足 \(x_{p1} \oplus x_{p2} \oplus \dots \oplus x_{pk} = a\) 。其中 \(a\) 是 \(0/1\) ,\(\oplus\) 是某种二元 \(bool\) 运算。
要求构造一种满足所有限制的变量的赋值方案。
\(2-SAT\) 问题是通过建立图论模型,在 \(O(n+m)\) 的时间复杂度内判断是否有解,若有解可以构造出一组合法解。
思路
值得注意的是在不同的题目中二元 \(bool\) 运算可能有差异,但是建图的基本思路大致相同。
来观摩一组 OI-Wiki
的例子:
比如邀请人来吃喜酒,夫妻二人必须去一个,然而某些人之间有矛盾(比如 \(A\) 先生与 \(B\) 女士有矛盾, \(C\) 女士不想和 \(D\) 先生在一起),那么我们要确定能否避免来人之间没有矛盾,有时需要方案。这是一类生活中常见的问题。
使用布尔方程表示上述问题。设 \(a\) 表示 \(A\) 先生去参加,那么 \(B\) 女士就不能参加( \(\lnot a\)); \(b\) 表示 C 女士参加,那么 \(\lnot b\) 也一定成立( \(D\) 先生不参加)。总结一下,即 \((a \lor b)\) (变量 \(a\) , \(b\) 至少满足一个)。对这些变量关系建有向图,则有:\(\lnot a \Rightarrow b \land \lnot b \Rightarrow a\) ( \(a\) 不成立则 \(b\) 一定成立;同理,\(b\) 不成立则 \(a\) 一定成立)。建图之后,我们就可以使用缩点算法来求解 \(2-SAT\) 问题了。
核心
Tarjan 缩点大法
\(Tarjan\) 大法好!
主要还是考虑如何更合适地建图。
再来一组例子:
假设有 \(a_1\) 、 \(b_2\) 和 \(a_2\) 、 \(b_1\) 两对,已知 \(a_1\) 和 \(b_2\) 间有矛盾,于是为了方案自洽,由于两者中必须选一个,所以我们就要拉两条有向边 \((a_1,b_1)\) 和 \((b_2,a_2)\) 表示选了 \(a_1\) 则必须选 \(b_1\) ,选了 \(b_2\) 则必须选 \(a_2\) 才能够自洽。
然后通过这样建边再跑一遍 \(Tarjan\) 判断是否有一个集合中的两个元素在同一个强连通分量中,若有则不可能,否则输出方案。构造方案只需要把几个不矛盾的强连通分量拼起来就好了。
输出方案时可以通过变量在图中的拓扑序确定该变量的取值。如果变量 \(\lnot x\) 的拓扑序在 \(x\) 之后,那么取 \(x\) 值为真。应用到 \(Tarjan\) 算法的缩点,即 \(x\) 所在强连通分量编号在 \(\lnot x\) 之前时,取 \(x\) 为真。因为 \(Tarjan\) 算法求强连通分量时使用了栈,所以 \(Tarjan\) 求得的强连通分量编号相当于反拓扑序。
时间复杂度为 \(O(n+m)\) 。
暴力DFS
\(Tarjan\) 好? \(DFS\) 表示不服。
\(DFS\) 大法妙!
直接选取图上一个点,沿着一条路径搜下去,如果一个点被选择了,那么这条路径以后的点都将被选择。
如果出现一个集合中的两者都被选择了,那么此即为矛盾情况。
例题
真·模板题
思路
这是一道模板题。
显而易见每个变量 \(x_i\) 都可以被分开存储,即拆分成 \(i\) 和 \(i+n\) ,分别表示 \(x_i=1\) 和 \(x_i=0\) ,则这两个事件是互斥的。
对于限制 \(x_i\) 的每个命题 \(a\) 和 \(b\) ,一定有一个为真,则可以写成
\lnot b \Rightarrow a
\]
那么由此可连边建图 \((\lnot a,b)\) , \((\lnot b,a)\) 。
在该图中,若节点 \(i\) 与 \(i+n\) 在同一个强连通分量中,即允许互相到达,则它们分别代表的互斥事件会同时发生,说明存在矛盾,即不存在一组合法的方案。
否则则有解。
构造合法解:
对原图缩点得到一张 \(DAG\) 。
对于变量 \(x_i\),考察节点 \(i\) 与 \(i+n\) 所在强连通分量的拓扑关系。若两分量不连通,则 \(xi\) 可取任意一个值。否则只能取属于拓扑序较大的分量的值。因为若取拓扑序较小的值,可以根据逻辑关系推出取另一个值也是同时发生的。
by @LuckyBlock
\(Tarjan\) 算法赋给强连通分量的编号顺序与拓扑序是相反的,上文已有说明。
CODE
/*
Name: P4782 【模板】2-SAT 问题
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;
/*=========================================快读*/
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x * f;
}
/*=====================================定义变量*/
int n, m;
const int _ = 5000050;
struct edge
{
int to;
int nxt;
} e[_];
int cnt, head[_];
int dfn[_], low[_], num;
int bel[_], b_num;
stack<int> s;
/*===================================自定义函数*/
void add(int from, int to)
{
e[++cnt].to = to;
e[cnt].nxt = head[from];
head[from] = cnt;
}
void Tarjan(int u_)
{
dfn[u_] = low[u_] = ++num;
s.push(u_);
for (int i = head[u_]; i; i = e[i].nxt)
{
int v_ = e[i].to;
if (!dfn[v_])
{
Tarjan(v_);
low[u_] = min(low[u_], low[v_]);
}
else if (!bel[v_])
low[u_] = min(low[u_], dfn[v_]);
}
if (dfn[u_] == low[u_])
{
b_num++;
while (true)
{
int t = s.top();
s.pop();
bel[t] = b_num;
if (t == u_)
break;
}
}
return;
}
/*=======================================主函数*/
int main()
{
n = read();
m = read();
for (int i = 1; i <= m; i++)
{
int x = read();
int a = read();
int y = read();
int b = read();
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, y + n);
add(y, x + n);
}
if (a && !b)
{
add(x + n, y + n);
add(y, x);
}
}
for (int i = 1; i <= n * 2; i++)
{
if (!dfn[i])
Tarjan(i);
if (i <= n && bel[i] == bel[i + n])
{
printf("IMPOSSIBLE\n");
return 0;
}
}
printf("POSSIBLE\n");
for (int i = 1; i <= n; i++)
printf("%d ", bel[i] < bel[i + n]);
return 0;
}
写在最后
最近被教练抓颓抓得好苦啊\kk
再次向已逝去的学长致敬(((不是
鸣谢:
《算法竞赛进阶指南》
@LuckyBlock
『笔记』2-SAT的更多相关文章
- 『TensorFlow』读书笔记_降噪自编码器
『TensorFlow』降噪自编码器设计 之前学习过的代码,又敲了一遍,新的收获也还是有的,因为这次注释写的比较详尽,所以再次记录一下,具体的相关知识查阅之前写的文章即可(见上面链接). # Aut ...
- 『TensorFlow』读书笔记_VGGNet
VGGNet网络介绍 VGG系列结构图, 『cs231n』卷积神经网络工程实践技巧_下 1,全部使用3*3的卷积核和2*2的池化核,通过不断加深网络结构来提升性能. 所有卷积层都是同样大小的filte ...
- 『TensorFlow』专题汇总
TensorFlow:官方文档 TensorFlow:项目地址 本篇列出文章对于全零新手不太合适,可以尝试TensorFlow入门系列博客,搭配其他资料进行学习. Keras使用tf.Session训 ...
- 『计算机视觉』各种Normalization层辨析
『教程』Batch Normalization 层介绍 知乎:详解深度学习中的Normalization,BN/LN/WN 一.两个概念 独立同分布(independent and identical ...
- 『AngularJS』$location 服务
项目中关于 $location的用法 简介 $location服务解析在浏览器地址栏中的URL(基于window.location)并且让URL在你的应用中可用.改变在地址栏中的URL会作用到$loc ...
- [原创] 【2014.12.02更新网盘链接】基于EasySysprep4.1的 Windows 7 x86/x64 『视频』封装
[原创] [2014.12.02更新网盘链接]基于EasySysprep4.1的 Windows 7 x86/x64 『视频』封装 joinlidong 发表于 2014-11-29 14:25:50 ...
- JS 中通过对象关联实现『继承』
JS 中继承其实是种委托,而不是传统面向对象中的复制父类到子类,只是通过原型链将要做的事委托给父类. 下面介绍通过对象关联来实现『继承』的方法: Foo = { // 需要提供一个 init 方法来初 ...
- 『摄影欣赏』16幅 Romantic 风格照片欣赏【组图】
今天,我们将继续分享人类情感的系列文章.爱是人类最重要的感觉,也可能是各种形式的艺术(电影,音乐,书,画等)最常表达的主题 .这里有40个最美丽的爱的照片,将激励和给你一个全新的视觉角度为这种情绪.我 ...
- 『开源』Slithice 2013 服务器集群 设计和源码
相关介绍文章: <『设计』Slithice 分布式架构设计-支持一体式开发,分布式发布> <『集群』001 Slithice 服务器集群 概述> <『集群』002 Sli ...
随机推荐
- hdu1828 Picture(线段树+扫描线+矩形周长)
看这篇博客前可以看一下扫描线求面积:线段树扫描线(一.Atlantis HDU - 1542(覆盖面积) 二.覆盖的面积 HDU - 1255(重叠两次的面积)) 解法一·:两次扫描线 如图我们可以 ...
- Codeforces Round #613 (Div. 2) B. Just Eat It! (DP)
题意:有一个长度为\(n\)的序列,找出最大的长度不为\(n\)的子段和,问最大子段和是否小于所有元素和. 题解:最大子段和我们可以直接用dp来找,每次状态转移为:\(dp[i]=max(dp[i-1 ...
- @Indexed 注解
本文转载自:https://www.cnblogs.com/aflyun/p/11992101.html 最近在看 SpringBoot 核编程思想(核心篇),看到走向注解驱动编程这章,里面有讲解到: ...
- .NET并发编程-函数闭包
本系列学习在.NET中的并发并行编程模式,实战技巧 内容目录 函数式编程闭包的应用记忆化函数缓存 函数式编程 一个函数输出当做另一个函数输入.有时候一个复杂问题,我们拆分成很多个步骤函数,这些函数组合 ...
- leetcode347 python
通过维护最小堆排序,使用heapq模块 一般使用规则:创建列表 heap = [] 函 数 ...
- codeforces 1039B Subway Pursuit【二分+随机】
题目:戳这里 题意:一个点在[1,n]以内,我们可以进行4500次查询,每次查询之后,该点会向左或向右移动0~k步,请在4500次查询以内找到该点. 解题思路:一边二分,一边随机. 交互题似乎有好多是 ...
- HDU 6611 K Subsequence(Dijkstra优化费用流 模板)题解
题意: 有\(n\)个数\(a_1\cdots a_n\),现要你给出\(k\)个不相交的非降子序列,使得和最大. 思路: 费用流建图,每个点拆点,费用为\(-a[i]\),然后和源点连边,和后面非降 ...
- vuepress config favicon
vuepress config favicon .vuepress/public favicons https://vuepress.vuejs.org/guide/assets.html#publi ...
- NMAP 使用教程!,nmap [Scan Type(s)] [Options] {target specification} , nmap -sn 192.168.2.0/24 , raspberry pi 3
NMAP 使用教程 https://nmap.org/man/zh/man-briefoptions.html 当Nmap不带选项运行时,该选项概要会被输出,最新的版本在这里 http://www.i ...
- Flutter CLI commands All In One
Flutter CLI commands All In One Flutter run key commands. r Hot reload. R Hot restart. h Repeat this ...