前置知识

强连通分量

k-SAT问题

k-SAT问题中的SAT意思就是(stability),也就是适应性问题。本意是给出n个变量,每一个变量有k个状态,并且也给出一些约束条件,要求你求出是否存在每一个变量的取值方案(状态分配方案)。

很可惜,k-SAT(k>2)已经被证明是NP完全的问题了,也就是说我们无法用一些多项式时间复杂度的算法来解决这个问题,但是我们可以发现。当k<=2的时候,我们可以用一些多项式时间复杂度的算法来解决这个问题。

2-SAT问题

先来一道模板题:

Luogu P4782

下面是对2-sat问题的一个例子

假如你是一个厨师,现在要给两个人做菜,每个人的口味都不同,而你至少要满足每个人的一个口味。这些人会提一些要求,比方说:

A:

  • 我不喜欢吃辣椒(-a)
  • 我喜欢吃肥肉有(b)

B:

  • 我不喜欢吃辣椒(-a)
  • 我不喜欢吃肥肉(-b)

那么我们可以用这种形式来简化表示约束条件:

A: -a or B: -a

A: b or B -b

对于每一个\(x_i\) or \(x_j\)的约束条件,我们可以将其变化成:

若\(x_i\)为假则\(x_j\)为真

若\(x_i\)为真则\(x_j\)为假

就比如说 A:-a or B:-b 可以变成

若 A:-a为假,则B: -b为真

若 B:-b为假,则A:-a为真

对上面的例子进行缩写,可以得到:

若A为a,则B为-b

若B为b,则A为-a

(因为-a为假的话证明肯定就是a了,反之亦然,同时也适用于b的情况)

于是,对于每一种约束条件的格式,我们都可以像上面这样分析,然后发现一些传递的关系:

我们把每一个变量抽象成图上的两个点,两个点分别代表了原来一个变量的两个不同的状态。也就是说变成a 和 -a (就是a真和a假)。

我们可以针对每一种约束条件把相对应的关系传递用有向边连接起来,表示从A的某一个状态可以推测出B的值(或者是b的某一个状态可以推得a的状态),也就是说这会形成一些链,在链上的每一个变量的值都是确定了的。但是根据数据的不同,实际上会出现强连通分量。并且如果一个强连通分量中包括某一个和原来a取值相反的-a的取值,则代表其矛盾,也就是说无解

我们这里使用tarjan求SCC(强连通分量)的算法来检测环的出现与否。

那么当问题有解的时候,我们该如何显示每一个变量的值呢?难道我们还需要对原图进行拓扑+染色吗?实际上我们并不需要这么做。因为我们在tarjan的过程中实际上就已经相当于求了一遍拓扑排序了。可以这么想,因为最后我们赋值scc的时候是从栈内弹出赋值的,也就是说越靠近叶子的节点越先被赋值,总的来看这不就是拓扑序的逆序吗?我们可以直接对比a和-a的拓扑序,哪个小就表明哪个是在越靠经根节点处被选中的,那么我们就优先选择哪个比较小的节点。

当然,因为这里我们存储的是拓扑的逆序,所以我们会优先选择哪个比较大的节点

一些代码处理的细节

虽然我们分析问题的时候经常会用a 或-a来表示某个变量的两种状态,但是数组的下标不能是负数。这里我们可以简单地表示-a为a+n,n就是点数。

如果完全按照每一种xi和xj的取值来写代码的话,就会是这个样子的:

for (int i=1;i<=m;i++) {
int a,va,b,vb;
scanf("%d %d %d %d",&a,&va,&b,&vb);
if (va && vb) { // a, b 都真,-a -> b, -b -> a
gpe[a+n].push_back(b);
gpe[b+n].push_back(a);
} else if (!va && vb) { // a 假 b 真,a -> b, -b -> -a
gpe[a].push_back(b);
gpe[b+n].push_back(a+n);
} else if (va && !vb) { // a 真 b 假,-a -> -b, b -> a
gpe[a+n].push_back(b+n);
gpe[b].push_back(a);
} else if (!va && !vb) { // a, b 都假,a -> -b, b -> -a
gpe[a].push_back(b+n);
gpe[b].push_back(a+n);
}
}

当然这么写是正确的,但是代码不可避免地有点冗长,这里我们可以使用位运算的写法可以缩短代码长度:

for(int i=1;i<=m;i++){
int a,va,b,vb;
scanf("%d %d %d %d",&a,&va,&b,&vb);
gpe[a+n*(va&1)].push_back(b+n*(vb^1));
gpe[b+n*(vb&1)].push_back(a+n*(va^1));
}

模板题AC代码:

//luogu p4782
#include <bits/stdc++.h>
using namespace std;
const int maxn=2000050;
struct edge{
int to;
edge(int to_){
to=to_;
}
};
vector<edge> gpe[maxn];
int dfn[maxn],low[maxn],ins[maxn],scc[maxn],size[maxn],cnt=0,sccn=0;
stack<int> s;
void tarjan(int u){
dfn[u]=low[u]=++cnt;
s.push(u);
ins[u]=1;
for(int i=0;i<gpe[u].size();i++){
int v=gpe[u][i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(ins[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
ins[u]=0;
scc[u]=++sccn;
size[sccn]=1;
while(s.top()!=u){
scc[s.top()]=sccn;
ins[s.top()]=0;
size[sccn]+=1;//这里的size实际上是不需要的
s.pop();
}
s.pop();
}
return;
}
int n,m,oud[maxn];
int main(void){
scanf("%d %d",&n,&m);
memset(low,0x3f,sizeof(low));
memset(ins,0,sizeof(ins));
for(int i=1;i<=m;i++){
int a,va,b,vb;
scanf("%d %d %d %d",&a,&va,&b,&vb);
gpe[a+n*(va&1)].push_back(b+n*(vb^1));
gpe[b+n*(vb&1)].push_back(a+n*(va^1));
}
for(int i=1;i<=n*2;i++){
if(!dfn[i]){
tarjan(i);
}
}
for(int i=1;i<=n;i++){
if(scc[i]==scc[i+n]){
printf("IMPOSSIBLE");
return 0;
}
}
printf("POSSIBLE\n");
for(int i=1;i<=n;i++){
printf("%d ",scc[i]<scc[i+n]);
}
return 0;
}

个人的xbb

其实这个问题感觉和差分约束有一点神似,因为都是根据关系来转换到图上解决的问题

看到自己以前写的代码感觉还是太蠢了qaq,但是反正也懒得改,就直接贴上去罢了

2-SAT问题简述的更多相关文章

  1. 简述WebService的使用(二)

    上集回顾 上一篇我简单的介绍了一下整个WebService建立和后端访问的过程,如果感兴趣可以看一看:简述WebService的使用(一) //如有不懂请留言,觉得有用请点赞 内容提要 这一篇主要介绍 ...

  2. 简述 OAuth 2.0 的运作流程

    本文将以用户使用 github 登录网站留言为例,简述 OAuth 2.0 的运作流程. 假如我有一个网站,你是我网站上的访客,看了文章想留言表示「朕已阅」,留言时发现有这个网站的帐号才能够留言,此时 ...

  3. JavaScript单线程和浏览器事件循环简述

    JavaScript单线程 在上篇博客<Promise的前世今生和妙用技巧>的开篇中,我们曾简述了JavaScript的单线程机制和浏览器的事件模型.应很多网友的回复,在这篇文章中将继续展 ...

  4. Design Patterns Simplified - Part 3 (Simple Factory)【设计模式简述--第三部分(简单工厂)】

    原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part3-factory/ Design ...

  5. Android网络定位服务定制简述

    Android 添加高德或百度网络定位服务 Android的网络定位服务以第三方的APK方式提供服务,由于在国内Android原生自带的com.google.android.gms服务几乎处于不可用状 ...

  6. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  7. 简述ASP.NET MVC原理

    1.为什么ASP.NET需要MVC? 因为随着网站的的数量级越来越大,原始的网站方式,这里指的是WebForm,在运行速度和维护性方面,以及代码量上面,越来越难以满足日益庞大的网站维护成本.代码的重构 ...

  8. Design Patterns Simplified - Part 2 (Singleton)【设计模式简述--第二部分(单例模式)】

    原文链接: http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part-2-singleton/ De ...

  9. 【翻译】设计模式学习系列1---【Design Patterns Simplified: Part 1【设计模式简述:第一部分】】

    原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part1/ Design Pattern ...

  10. Android开发3:Intent、Bundle的使用和ListView的应用 、RelativeLayout(相对布局)简述(简单通讯录的实现)

    前言 啦啦啦~博主又来骚扰大家啦~大家是不是感觉上次的Android开发博文有点长呢~主要是因为博主也是小白,在做实验的过程中查询了很多很多概念,努力去理解每一个知识点,才完成了最终的实验.还有就是随 ...

随机推荐

  1. C1FlexGrid双grid滚动条联动

    利用AfterScroll事件,来实现双grid联动. private void Grid_AfterScroll(object sender, RangeEventArgs e) { try { i ...

  2. Inno Setup 添加版权信息

    [Setup]AppCopyright=Copyright (C) - My Company, Inc. 有以上一句,即可在右键 --> Property --> Details 里看见版 ...

  3. linux uniq 命令实用手册

    Linux uniq 命令用于处理文本内容中的重复行. 这里我们只介绍其常用参数,其完整用法可参见man uniq. 例如,我们有如下文件内容: >>> cat log.txt __ ...

  4. from _sqlite3 import * ImportError: DLL load failed: 找不到指定的模块。

    *Error creating Django application: Error on python side. Exit code: 1, err: Traceback (most recent ...

  5. 快速上手最新的 Vue CLI 3

    翻译:疯狂的技术宅 原文:blog.logrocket.com/getting-sta- 概要:本文将指导你快速上手 Vue CLI 3,包括最新的用户图形界面和即时原型制作功能的使用步骤. 介绍 尤 ...

  6. JQuery学习(一)

    本文是学习廖老师的Javascript全栈教程后的一些笔记. 使用jQuery: 方法一:下载jQuery库,并在html页面中引入,方式如下: 1 <html> 2 <head&g ...

  7. 数学--数论--HDU 5223 - GCD

    Describtion In mathematics, the greatest common divisor (gcd) of two or more integers, when at least ...

  8. P4932 浏览器(统计二进制1的个数)

    P4932 浏览器 有\(n\)个数,\(x_1,x_2,\cdots,x_n\),问你有多少对\((u,v)\),使得\(x_u\operatorname{xor}x_v\)的二进制表示中有奇数个\ ...

  9. [js进阶1]-数据类型

    基本数据类型 js 总的有7中数据类型,包括基本类型和引用类型 基本类型 6 种 number boolean string null undefiend symbol 前5种类型统称为原始类型 sy ...

  10. Java变量相关

    1.Java是强类型语言 所有的变量必须先声明,后使用: 指定类型后只能接受类型匹配的值: 2.变量声明 变量标识符由字母.数字.下划线和$组成: 关键字和保留字不能做标识符: 长度不限制: 大小写区 ...