文章目录

  • 参考资料与前话
  • sat问题简介
  • 解法
    • 例题
    • 解法以及一点点证明
      • 基本的转换思路
      • 基本的构图
      • 基础解法1
      • 证明1
      • 代码1
      • 基础解法2
      • 基础证明2
      • 代码2
  • 小结

参考资料与前话

luogu题解

伍昱奆佬的PPT Orz

顺便说一下,如果看这篇文章的人有一些较高深的2-sat姿势如果可以的话发个评论,感觉我这篇文章比较浅。

由于证明总是很难一块讲完讲另一块,都是互相息息相关的,所以有的证明有时候是有误在后面再根据已经讲的漏洞修改,希望大家习惯。

sat问题简介

以前,你妈总是问你哪个是对的,哪个是错的,现在,老师也总是问你,哪个是对的,哪个是错的。

不过不再是1+1=2是不是对的这种幼稚的问题,而是对于一些不知道具体内容的命题,只知道他们之间存在一些逻辑问题,让你判断他们的真假。

现在有很多命题 q 1 , q 2 , q 3 . . . , q n q_1,q_2,q_3...,q_n q1​,q2​,q3​...,qn​

给你一些他们的逻辑关系:

q i q_i qi​ o r or or q j = 0 q_j=0 qj​=0

q k q_k qk​ a n d and and q k = 1 q_k=1 qk​=1

然后判断每个命题的真假,这种问题就叫sat问题。

特别的,如果对于所有的逻辑关系的式子中如果都是 k k k个元的,那么就是 k − s a t k-sat k−sat问题。

当然,已经有人证明了 k − s a t ( k > 2 ) k-sat(k>2) k−sat(k>2)的问题是NPC问题。

不过 k = 2 k=2 k=2反倒是个特例,我们把他们叫做 2 − s a t 2-sat 2−sat问题。 这个问题是可以线性解决的。

曾经我看到一个奆佬说过:含有一定结构的 n-SAT 问题可以被新的 SAT solver 挺快的解决,但是完全随机的 n-SAT 问题就很难解决。

这个问题,我们讨论的就是 2 − s a t 2-sat 2−sat问题。

解法

例题

例题

万能的luogu

解法以及一点点证明

基本的转换思路

这里,我们仅仅讨论例题里面涉及的逻辑关系,其他的都自己想吧(╯‵□′)╯︵┻━┻。

现在题目基本上就是给你 a a a o r or or b = 1 b=1 b=1,然后进行判断。

但是需要注意一个事情,就是是可以 a a a o r or or a = 1 / 0 a=1/0 a=1/0,也就是指定了 a a a一定是 0 / 1 0/1 0/1了。

2 − s a t 2-sat 2−sat和差分约束一样都是利用构图来完成的, 2 − s a t 2−sat 2−sat基本思路是拆点,我们先把第 i i i个命题拆成 x i x_{i} xi​和 ! x i !x_{i} !xi​ (一下规定,如果带有 x i x_i xi​表示的是 1 1 1, ! x i !x_i !xi​是 0 0 0,但如果是对于一个点 a a a, ! a !a !a则是 a a a所属命题的另外一个值),如果我们选了 x i x_i xi​就是这个命题为 1 1 1,选 ! x i !x_{i} !xi​就表示这个命题为 0 0 0,我们现在规定如果存在一条边, a − > b a->b a−>b,那么就表示如果我们选了 a a a,那么也必须一起把 b b b选了。

那么什么情况下某种方案不成立呢?简单明了,如果 x x x和 ! x !x !x同时被选中不就是这种方案不成立了吗。

基本的构图

也许许多人看到这个定义是蒙的,这个要怎么构图?

举个例子其他自己推吧。

q x q_x qx​ o r or or q y = 1 q_y=1 qy​=1

那么就表示 x , y x,y x,y里面至少要有一个选 1 1 1号点,也就是说如果我的 x x x命题选了 0 0 0, y y y必须选 1 1 1,反之也是如此。

也就是 ! x x − > x y , ! x y − > x x !x_{x}->x_y,!x_y->x_x !xx​−>xy​,!xy​−>xx​。

其他的自己推,当然也有一个特例,就是 q i q_i qi​ o r or or q i = 1 qi=1 qi=1,也就是必须为 1 1 1。

我们从建图的角度讲,我们就是要让图里面每次选到 ! x i !x_{i} !xi​都不成立,不成立的条件是什么呀?

不就是两个点都被选到吗,那么只要 ! x i − > x i !x_i->x_i !xi​−>xi​,不是就可以保证 ! x i !x_i !xi​绝对不成立了吗?

至此,建图讲完了。

基础解法1

这个解法其实就是暴力,超级的暴力,对于每个点,如果 0 0 0或者 1 1 1没被选到,就选它 0 / 1 0/1 0/1,然后让他跑一边DFS传递,如果没问题,就让他当 0 / 1 0/1 0/1(经过的点不用重置),有问题,就将这次DFS所修改的点全部重置,然后跑另外一个值,都不成立,无解。

时间复杂度个人认为是 O ( n 2 ) O(n^2) O(n2),但是常数小,思路简单。

证明1

如果你对解法1心存疑虑,那么多半是担心如果选 1 1 1成立,就选 1 1 1,而不考虑 0 0 0,这个操作有没有可能有后效性。

这里画个图帮助大家理解一下这种冲突

这个图一坨人会说,有解啊, 0 , 1 0,1 0,1或 0 , 0 0,0 0,0,反正你开始选 1 1 1就是个错误的选择,有后效性懂不懂!

但是对于 2 − s a t 2-sat 2−sat问题,目前我所知道的操作都是对称的,也就是说若 a − > b a->b a−>b,则有 ! b − > ! a !b->!a !b−>!a,当然除了自己连自己以外(这个情况也是无伤大雅,不用理他),所以这个图要么不成立,要么少了两条边。

所以我们对于这个方法是错的的疑虑只是因为说怕选了 1 1 1但是后面的点都是指向 0 0 0的,但是你不曾考虑过如果后面有点指向 ! x i !x_i !xi​, x i x_i xi​也会指回那个点的反点(即 ! a !a !a和 a a a的关系)。

考虑 a − > ! x i a->!x_i a−>!xi​,那么 x i − > ! a x_i->!a xi​−>!a,所以不需要担心有后效性,因为我只要选了 1 1 1, ! a !a !a点也会被选,怎么可能轮得到 a a a来说话呢?

代码1

注:这份代码是把 x i x_i xi​当成 0 0 0了。

#include<cstdio>
#include<cstring>
#define  N  2100000
#define  M  4100000
using  namespace  std;
struct  node
{int  x,y,next;
}a[M];int  len,last[N];
inline  void  ins(int  x,int  y)
{len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;
}
int  n,m,col[N]/*1表示被选,2表示等待选择*/;
int  list[N],tail;
int  fan(int  x){return  x>n?x-n:x+n;}
bool  dfs(int  x)//从x开始递归
{if(col[x]==1)return  true;else  if(col[fan(x)]==1)return  false;col[x]=1;list[++tail]=x;//以后方便回溯for(int  k=last[x];k;k=a[k].next){int  y=a[k].y;if(dfs(y)==false)return  false;}return  true;
}
int  ans[N];
int  main()
{//  freopen("std.in","r",stdin);
//  freopen("vio.out","w",stdout);我就是个SB东西,这都要对拍scanf("%d%d",&n,&m);for(int  i=1;i<=m;i++){int  x,y,a,b;scanf("%d%d%d%d",&x,&a,&y,&b);int  xx=fan(x),yy=fan(y);if(a==1)x^=xx^=x^=xx;if(b==1)y^=yy^=y^=yy;if(x==y){if(a==b)ins(xx,x);} else  ins(xx,y),ins(yy,x);//这个构图可以过,但是十分乱七八糟,如果你有更好的还是用自己的吧}for(int  i=1;i<=n;i++){if(col[i]==0  &&  col[fan(i)]==0){tail=0;if(dfs(i)==0){while(tail)col[list[tail--]]=0;if(dfs(i+n)==0){printf("IMPOSSIBLE\n");return  0;}else  ans[i]=1;}} else  ans[i]=(col[i]==0);}printf("POSSIBLE\n");for(int  i=1;i<n;i++)printf("%d ",ans[i]);printf("%d\n",ans[n]);return  0;
}

基础解法2

这个做法可就是线性 O ( n ) O(n) O(n)的了。

观察到一个强连通分量里面选了一个就全部都被选,那么我只需要作个tarjan缩点求出强连通不就简单很多了吗。

这里科普一下强连通的联通块编号其实就是拓扑序的反序,即入度为 0 0 0的编号最大。

这里随便乱说一通,说错了证明供上

如果 a a a和 ! a !a !a在一个联通块,当场无解, 然后对于 a a a和 ! a !a !a,哪个所在的拓扑序编号大选哪个(代码表现为所在连通块编号越小选哪个)。

基础证明2

首先说明,对于 a − > ! a a->!a a−>!a这条边不影响我们考虑对称性是因为如果他影响我们考虑对称性即他有影响强连通的个数的话,那么就代表 a , ! a a,!a a,!a在一个联通块里面,就代表无解,而我们讨论的都是有解情况,直接忽视。

这里无聊的证明一个东西,就是在有解情况下,一个点的联通块及其反点的联通块的点的个数相同,且里面每个点都能在对面集合找到自己的反点。

这个运用对称性好好想想就知道了。

这里上个图:

那联通块编号是拓扑序反序又是怎么理解啊。

脱开2-sat,放到一般图中:

拓扑序满足的是什么,对于一个点 a a a,指向 a a a的点 b b b的拓扑序肯定在 a a a之前,但是 t a r j a n tarjan tarjan刚好满足的是若 b − > a b->a b−>a且 a , b a,b a,b不同联通块,则 a a a的联通块编号肯定比 b b b小,所以刚好相反。

好了,现在证明最后一句话,为什么选拓扑序大的,也就是为什么选这个点所在联通块TJ(tarjan)序小的。

我们先假设 x x x的拓扑序大于 ! x !x !x,这样 x x x是会被选择的,但是我们需要证明 x x x能到达的点都被选了。

开始证明:
假设 x x x的拓扑序小于 ! x !x !x(反过来,方便打符号),这样选的是 ! x !x !x,设能走到 x x x点的点为 y y y,那么需要证明, y y y也不会被选。

因为 y y y的拓扑序小于 x x x小于 ! x !x !x小于 ! y !y !y,所以只会选择 ! y !y !y,不会选择 y y y。

至于拓扑序相等的情况,随便选一个就行了, x − > ! x x->!x x−>!x的情况,无伤大雅,只要不影响对称性即可。

代码2

#include<cstdio>
#include<cstring>
#define  N  2000050
using  namespace  std;
inline  void  myre(int  &x)
{x=0;char  c=getchar();while(c>'9'  ||  c<'0')c=getchar();while(c>='0'  &&  c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();}
struct  node
{int  y,next;
}a[N];int  len,last[N];
void  ins(int  x,int  y)
{len++;a[len].y=y;a[len].next=last[x];last[x]=len;
}
int  n,m,vis[N],sta[N],num,scc[N]/*所在的联通块*/,tim,list[N],top;
inline  int  mymin(int  x,int  y){return  x<y?x:y;}
void  dfs(int  x)
{vis[x]=sta[x]=++tim;list[++top]=x;for(int  k=last[x];k;k=a[k].next){int  y=a[k].y;if(!vis[y]){dfs(y);vis[x]=mymin(vis[x],vis[y]);}else  if(!scc[y])vis[x]=mymin(vis[x],sta[y]);}if(vis[x]==sta[x]){num++;scc[x]=num;while(list[top]!=x)scc[list[top]]=num,top--;top--;//把自己除出去 }
}
inline  int  fan(int  x){return  x>n?x-n:x+n;}
int  main()
{myre(n);myre(m); for(int  i=1;i<=m;i++){int  x,y,a,b;myre(x);myre(a);myre(y);myre(b);int  xx=fan(x),yy=fan(y);if(a==1)xx^=x^=xx^=x;if(b==1)yy^=y^=yy^=y;if(x==y){if(a==b)ins(xx,x);}else  ins(xx,y),ins(yy,x);}for(int  i=1;i<=2*n;i++){if(!scc[i]/*没有阵营*/)dfs(i);}//全部都分好了阵营。for(int  i=1;i<=n;i++) {if(scc[i]==scc[fan(i)]){printf("IMPOSSIBLE\n");return  0;}}printf("POSSIBLE\n");for(int  i=1;i<=n;i++){if(scc[i]<scc[fan(i)])//拓扑序小的先{printf("0 ");} else  printf("1 ");}return  0;
}

小结

没做什么题目,溜了溜了。

2-SAT问题,一个神奇的东西相关推荐

  1. cocoapods的安装(这真是一个神奇的东西,每次安装的方法都不一样,而且很容易出现各种各样的错误)...

    文章开始之前,建议安装一个显示网速的插件,不然你不知道到底有没有下载,也让生活有一点盼头 1.因为众所周知的原因(我dang的行为真的是让人失望),先更换一下ruby镜像源 $ gem sources ...

  2. 23,148,855,308,184,500是一个神奇的数字,还是纯粹的机会?

    新闻报道等这一指示上述号码可能出现的编程错误. 一名男子在美国突然出现在当地的加油站购买一包香烟 - 但却发现他的信用卡收费为23,148,855,308,184,500美元. 这是23亿美元(14亿 ...

  3. 计算机图标怎么隐藏cmd,一个神奇的bat批处理文件,更好的隐藏电脑里的文件或者文件夹...

    原标题:一个神奇的bat批处理文件,更好的隐藏电脑里的文件或者文件夹 在之前和大家分享了如何隐藏电脑上不希望别人看到的文件或者文件夹,具体的分享了两种方法: 1.利用文件或者文件夹本身的隐藏属性进行隐 ...

  4. 淘宝新品补单平台怎么样才能为你的产品精准打标?大神导航,一个神奇的网站,从此开启大神之路!

    不看你一定会后悔! 大神导航,一个神奇的网站,从此开启大神之路! 轻松导航,不仅有最新资讯,还有最近使用记录.常用推荐,方便用户使用!图标精美,良心精美.简易搜索,功能齐全,免费高质量素材任意挑选! ...

  5. 淘宝详情页排版布局怎么做?大神导航,一个神奇的网站,从此开启大神之路!

    不看你一定会后悔! 大神导航,一个神奇的网站,从此开启大神之路! 轻松导航,不仅有最新资讯,还有最近使用记录.常用推荐,方便用户使用!图标精美,良心精美.简易搜索,功能齐全,免费高质量素材任意挑选! ...

  6. 淘宝标题核心关键词怎么做?大神导航,一个神奇的网站,从此开启大神之路!

    [网站推荐] 不看你一定会后悔! 大神导航,一个神奇的网站,从此开启大神之路! 轻松导航,不仅有最新资讯,还有最近使用记录.常用推荐,方便用户使用!图标精美,良心精美.简易搜索,功能齐全,免费高质量素 ...

  7. 淘宝补单可以补金币数据吗?大神导航,一个神奇的网站,从此开启大神之路!

    不看你一定会后悔! 大神导航,一个神奇的网站,从此开启大神之路! 轻松导航,不仅有最新资讯,还有最近使用记录.常用推荐,方便用户使用!图标精美,良心精美.简易搜索,功能齐全,免费高质量素材任意挑选! ...

  8. 淘宝人群拖价怎么做? 大神导航,一个神奇的网站,从此开启大神之路!

    不看你一定会后悔! 大神导航,一个神奇的网站,从此开启大神之路! 轻松导航,不仅有最新资讯,还有最近使用记录.常用推荐,方便用户使用!图标精美,良心精美.简易搜索,功能齐全,免费高质量素材任意挑选! ...

  9. 淘宝聚划算怎么做?大神导航,一个神奇的网站,从此开启大神之路!

    不看你一定会后悔! 大神导航,一个神奇的网站,从此开启大神之路! 轻松导航,不仅有最新资讯,还有最近使用记录.常用推荐,方便用户使用!图标精美,良心精美.简易搜索,功能齐全,免费高质量素材任意挑选! ...

最新文章

  1. 【建模必备】遗传算法的基本原理与步骤(交叉)
  2. java通过jdbc登陆系统_JDBC模拟登录
  3. 深入了解数据人才 | 中国数据人才白皮书
  4. LP Wizard 10.5破解步骤
  5. 函数计算助力闲鱼构建云端一体化变成模式
  6. LDAP Schema的概念和基本要素
  7. 中石油训练赛 - Fermat‘s Optimization Problem(Java高精度运算+二分)
  8. RxJava操作符serialize 笔记-二十六
  9. 程序员 数学_程序员数学课程
  10. BUUCTF web writeup
  11. Centos任务栏不见了
  12. mysql之(1366,Incorrect string value:'\\xF0\\x9F\\x98\\x82...' for column 'content' at row 1)20
  13. Springboot定时任务【多线程处理】
  14. 使用Unity编写传统ARPG游戏人物操作方式(二)
  15. 计算机硬件知识比赛策划,计算机硬件知识讲座活动策划案.doc
  16. Kafka SCRAM和PLAIN权限认证
  17. 丘成桐中学生计算机科学竞赛,丘成桐计算机奖介绍,为什么要参加丘成桐奖?...
  18. Google结构化数据
  19. 应届毕业生Android面试经验小谈
  20. Linux系统中的ddns:dhcp+dns=ddns (花生壳)

热门文章

  1. Uber H3 index 地图索引思考
  2. java使用ffmpeg将视频转码
  3. vue 点击当前路由怎么重新加载_Vue 路由切换时页面内容没有重新加载的解决方法...
  4. 数据库实验四:数据高级查询
  5. **蒙特卡洛计算定积分VC++**
  6. 如何高效地从BAM文件中提取fastq
  7. 华为鸿蒙新闻短评,科技圈“某高管”发表对华为鸿蒙的看法,遭网友回怼
  8. YY创始人携手极客公园调坎QQ企鹅
  9. 如何自建微信外卖平台_本地外面平台怎么起步,如何自建微信外卖平台
  10. HTML5接入百度地图并搜索定位