2-sat小结


原文作者:老K

原文传送门


2-sat是什么

一类问题是这样的:

(两个符号的意思 \(\lor \ or,\land \ and\))

有n个布尔变量,现在对它们做出限制,比如\(a_i=1,a_i \lor a_j=1\),求一组可行的解。

假设限制元素最多的限制限制了k个元素,这个问题就被称为k-sat问题。

可以证明(然而我不会),\(k>2\)时是NPC的。


基础想法

把每个变量\(a_i\)拆成2个点\(i_0,i_1\),表示它为1或0

每个变量就变成了一个集合。要求在每个集合里选一个元素,满足所有限制。

然后有向边\(<u,v>\)表示若选u则选v。

\(a_i=1\)型限制

连\(<i_0,i_1>\)。

\(a_i=0\)则反过来。

\(a_i=a_j\)限制

连\(<i_0,j_0><j_0,i_0><i_1,j_1><j_1,i_1>\)

\(a_i\not=a_j\)限制

连\(<i_0,j_1><j_0,i_1><i_1,j_0><j_1,i_0>\)

\(a_i\lor a_j=1\)或者\(a_i\land a_j =0\)限制

对于前者,连\(<i_0,j_1><j_0,i_1>\)

后者显然是交换0,1

\(a_i\lor a_j=0\)或者\(a_i\land a_j =1\) 限制

拆开,转化为\(a_i=1\)型限制

其实都很显然是吧。


算法1

有一个很显然的结论:设\(u'\)表示u所在集合的另一个元素。

如果存在\(<u,v>\)则一定存在\(<v',u'>\)(对称性)

那么我们可以按照顺序枚举每一个布尔变量。

假设它是0,然后沿着单向边计算它影响的元素,如果没有问题就是0,并且计算它影响的元素的值。

否则假设它是1,如果没问题就是1,并且计算它影响元素的值。

否则无解。

由于对称性以及边是单向的,可以证明这个算法的正确性。(或者感性理解)

时间复杂度\(O(nm)\)其中n是点数m是边数。

当然0,1的顺序是可以交换的。

这个算法可以求出字典序最小的解。

在算之前,按照每个变量0,1编号较小值从小到大排序。

然后枚举要先枚举每个变量编号较小的那个点。


算法2

有没有更快的算法呢?

当然是有的。

我们可以先tarjan 强连通分量缩点。

那么同一个强连通分量里,点的选择情况是一样的(选则都选,不选则都不选)。

由对称性可知如果\(u,v\)在同一个强连通分量里,那么\(u',v'\)也在同一个强连通分量里。

如果\(u,u'\)在同一个强连通分量里,那么无解。

否则考虑,缩完点后得到了一个DAG,由两个边相反的DAG组成。

我们可以通过拓扑排序,每次找一个没有出边的分量,如果它里面点的对称点没有被选择,那么执行如下操作:

选择它里面的所有点。

把它扔掉。(把所有到它的边断掉)

可以发现是对的。

时间复杂度\(O(n+m)\),通常用来计算可行解或者判断可行性。


算法3(黑科技)

不难发现,在缩完点后,如果u能到达v,那么在tarjan 中,u一定在v之后被找到。

于是我们可以在拓扑排序中,每次取能取的,编号最小的分量,

显然,如果一个分量\(u\)和分量\(v\)是对称的,那么如果\(u<v\),会选择u,否则会选择v。

那么就不用重建图拓扑排序了。

常数优化/代码量优化效果还是很明显的。


例题

满汉全席

模板题。


NOI 2017 Day2T1 游戏

之所以用UOJ是因为ex test巨强无比。

发现有一些地图是X,3种都可以,但是个数不超过8.

我们可以把X转化为A或B。

枚举每个X转化为A还是B,跑2-SAT就可以了。

/*
Authlor: CNYALI_LK
LANG: C++
PROG: 317.cpp
Mail: cnyalilk@vip.qq.com
*/
#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define all(x) x.begin(),x.end()
using namespace std;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
typedef pair<int,int> pii;
template<class T>int chkmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>int chkmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
#define min mmin
#define max mmax
#define abs aabs
int read(){
int s=0,base=1;
char c;
while(!isdigit(c=getchar()))if(c=='-')base=-base;
while(isdigit(c)){s=s*10+(c^48);c=getchar();}
return s*base;
}
char rc(){
char c;
while(!isalpha(c=getchar()));
return c;
}
void rs(char *s){
while(!isalpha(*s=getchar()))++s;
while(isalpha(*s))*(++s)=getchar();
*s=0;
}
char WriteIntBuffer[1024];
template<class T>void write(T a,char end){
int cnt=0,fu=1;
if(a<0){putchar('-');fu=-1;}
do{WriteIntBuffer[++cnt]=fu*(a%10)+'0';a/=10;}while(a);
while(cnt){putchar(WriteIntBuffer[cnt]);--cnt;}
putchar(end);
}
struct _limit{
int a,b,ax,bx;
};
_limit a[102424];
char s[102424];
_limit limit(){
_limit x;
x.a=read()-1;
x.ax=rc();
x.b=read()-1;
x.bx=rc();
return x;
}
int h[102424];
int beg[102424],to[233333],lst[233333],dfn[102423],low[102423],no[102423];
int blocks,e,t;
void add(int u,int v){
to[++e]=v;
lst[e]=beg[u];
beg[u]=e;
}
int stk[102424],*top=stk;
void dfs(int x){
dfn[x]=low[x]=++t;
*(++top)=x;
flor(int i=beg[x];i;i=lst[i])
if(dfn[to[i]]){
if(!no[to[i]])
chkmin(low[x],dfn[to[i]]);
}else{
dfs(to[i]);
chkmin(low[x],low[to[i]]);
}
if(dfn[x]==low[x]){
++blocks;
do{
no[*(--top+1)]=blocks;
}while(*(top+1)!=x);
}
} int main(){
#ifdef cnyali_lk
freopen("317.in","r",stdin);
freopen("317.out","w",stdout);
#endif
int n,d,m;
n=read();
d=read();
rs(s);
int xs=0;
flor(int i=0;i<n;++i){
if(s[i]=='x'){h[xs]=i;++xs;}
}
m=read();
flor(int i=1;i<=m;++i){
a[i]=limit();
}
int un=1<<d,u,v;
flor(int j=0;j<un;++j){
flor(int i=0;i<d;++i)s[h[i]]='a'+!!(j&(1<<i));
flor(int i=0;i<n+n;++i)beg[i]=dfn[i]=low[i]=no[i]=0;
blocks=e=t=0;
flor(int i=1;i<=m;++i){
if(a[i].ax-'A'==s[a[i].a]-'a')continue;
if(a[i].bx-'A'==s[a[i].b]-'a'){
int u=a[i].a*2+(a[i].ax-'A'-(a[i].ax-'A'>=s[a[i].a]-'a'));
add(u,u^1);
}
else{
int u=a[i].a*2+(a[i].ax-'A'-(a[i].ax-'A'>=s[a[i].a]-'a'));
int v=a[i].b*2+(a[i].bx-'A'-(a[i].bx-'A'>=s[a[i].b]-'a'));
add(u,v);
add(v^1,u^1);
}
}
flor(int i=0;i<n+n;++i)if(!dfn[i])dfs(i);
int ok=1;
flor(int i=0;i<n;++i)if(no[i<<1]==no[i<<1|1])ok=0;
if(ok){
flor(int i=0;i<n;++i){
int ans=(no[i<<1]>no[i<<1|1]);
ans+=(ans>=s[i]-'a');
putchar(ans+'A');
}
return 0;
}
}
printf("-1\n");
return 0;
}

\({\Huge\color{Gold}{老K太强了QAQ}}\)

(转)2-SAT小结的更多相关文章

  1. python datetime 时间日期处理小结

    python datetime 时间日期处理小结 转载请注明出处:http://hi.baidu.com/leejun_2005/blog/item/47f340f1a85b5cb3a50f5232. ...

  2. python formatters 与字符串 小结 (python 2)

    最近学习python 2 ,觉得有必要小结一下关于字符串处理中的formatters, 转载请声明本文的引用出处:仰望大牛的小清新 0.%进行变量取值使用的时机 在python中,如果我们只是需要在字 ...

  3. 从零开始编写自己的C#框架(26)——小结

    一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...

  4. Python自然语言处理工具小结

    Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...

  5. java单向加密算法小结(2)--MD5哈希算法

    上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...

  6. iOS--->微信支付小结

    iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...

  7. iOS 之UITextFiled/UITextView小结

    一:编辑被键盘遮挡的问题 参考自:http://blog.csdn.net/windkisshao/article/details/21398521 1.自定方法 ,用于移动视图 -(void)mov ...

  8. K近邻法(KNN)原理小结

    K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...

  9. scikit-learn随机森林调参小结

    在Bagging与随机森林算法原理小结中,我们对随机森林(Random Forest, 以下简称RF)的原理做了总结.本文就从实践的角度对RF做一个总结.重点讲述scikit-learn中RF的调参注 ...

  10. Bagging与随机森林算法原理小结

    在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...

随机推荐

  1. 异常:java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path '/app/userInfoMaint/getProvince.do'

    调试代码时出现异常:java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path '/app/user ...

  2. Catch That Cow(bfs)

    Description Farmer John has been informed of the location of a fugitive cow and wants to catch her i ...

  3. 10-EasyNetQ之控制队列名称

    EasyNetQ默认行为,当生成队列的名称时,使用消息类型名+subscription Id.例如:PartyInvitation 这个消息类型,命名空间为 EasyNetQ.Tests.Integr ...

  4. java Web jsp四大作用域和九大内置对象

    JSP中的四大作用域:page.request.session.application 这四大作用域,其实就是其九大内置对象中的四个,为什么说他们也是JSP的四大作用域呢?因为这四个对象都能存储数据, ...

  5. 用rand5()生成rand(n)

    问题:有函数rand5(),它能够等概率生成[0,5)之间的整数.由rand5()构造rand(n)使其能够等概率生成[0,n)之间的整数. 思路1:有rand5()先生成等概率生成0和1的rand0 ...

  6. Angular23 loading组件、路由配置、子路由配置、路由懒加载配置

    1 需求 由于Angular是单页面的应用,所以在进行数据刷新是进行的局部刷新:在进行数据刷新时从浏览器发出请求到后台响应数据是有时间延迟的,所以在这段时间就需要进行遮罩处理来提示用户系统正在请求数据 ...

  7. 如何学习MySQL

    转自高手的帖子 1.坚持阅读官方手册,看MySQL书籍作用不会特别大:(挑选跟工作相关的内容优先阅读,例如InnoDB存储引擎,MySQL复制,查询优化) 2.阅读官方手册,同时对阅读的内容做对应的测 ...

  8. c语言学习笔记 if语句的条件判断

    可能经常会看到错误的if语句示范,比如这样的: if(a=6) { printf("hello"); } if语句块执行的条件是if条件的运算结果不是0则执行if语句块. a=6这 ...

  9. 28. LAST() 函数

    LAST() 函数 LAST() 函数返回指定的字段中最后一个记录的值. 提示:可使用 ORDER BY 语句对记录进行排序. SQL LAST() 语法 SELECT LAST(column_nam ...

  10. 怎样去阅读一份php源代码

    一份好的源代码例如 dz的论坛,wind论坛,帝国cms,dedecms等,都具有自己的一套设计思路和设计模式,所以在看某个产品之前就要做好心理准备,可以把自己的经验和这些产品做对比,但千万别一直用自 ...