2-SAT

推荐blog:

https://www.mina.moe/archives/11387

https://www.cnblogs.com/cjjsb/p/9771868.html

 

SAT是适定性(Satisfiability)问题的简称 。一般形式为k-适定性问题,简称 k-SAT

可以证明,当k>2时,k-SAT是NP完全的。因此一般讨论的是k==2的情况,即2-SAT问题。

2-SAT用处 :有若干个包含2个元素的集合,只能选择每个集合中的一个元素,并给出一些条件(例:选a后不能选b),要求求出满足这些条件的选择,这个解也称为2-SAT的解

 

如何求解?

 

如上图,为{A,A'},{B,B'},{C,C'}这几个集合

若给出条件,{A,B'},{B‘C’}不能同时选择

因为{A,A'},{B,B'},{C,C'}中,每个集合都要选出一个元素,即不能同时选择

又因为{A,B'}不能同时选择,则选择B‘时,一定选择A’,于是在B‘,A’之间连上一条有向边(因为选A‘不一定B’);同理,在选B时也一定选A

其它也同上处理,于是:

接着再进行缩点,然而这个栗子中没有可缩的点,判断对称的两点是否在同一个强连通分量中,若在,则无解

若不在,我们接着应该求出符合要求的解,如何求呢

1.进行拓扑排序(不用了)我们可以发现,缩点后强连通分量的编号就是反着的拓扑排序

为什么要拓扑排序呢?比如一个样例,它让A->B->A',显然我们不能选A,只能选A’(拓扑排序大的),即选强连通分量编号小的

2.对于每一个集合,选出强连通分量小的

 

那为什么选小的一定能满足之前上面所有的条件呢?

可以这样想:

一个点如果被选了,那么选它而必须选的点,因为连了边,

所以,在遍历此点时也会将其他必须选的点赋上颜色,

然而下一次,从另一个点开始遍历时,选这个点而会被选上的点,又会被赋值

然而这一次被赋上的值的点 的值,一定比上一次赋上值的点 的值 要大

所以挑最小的一定能满足所有的条件

 

推荐题:luogu P4782

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int head[2000006],zhan[2000006],color[2000006],dfn[2000006],low[2000005];
 4 int num,Color,dfx,top;
 5 bool pd[2000005];
 6 struct{
 7     int to,next;
 8 }a[2000006];
 9 void lian(int from,int to){
10     num++;
11     a[num].to=to;
12     a[num].next=head[from];
13     head[from]=num;
14 }
15 void tarjan(int x){
16     int i;
17     dfn[x]=low[x]=++dfx;zhan[++top]=x;
18     pd[x]=1;
19     for(i=head[x];i;i=a[i].next){
20         if(pd[a[i].to]==0){
21             tarjan(a[i].to);
22             low[x]=min(low[x],low[a[i].to]);
23         }
24         if(color[a[i].to]==0)low[x]=min(low[x],dfn[a[i].to]);
25     }
26     if(low[x]==dfn[x]){
27         Color++;
28         do{
29             i=zhan[top];top--;            
30             color[i]=Color;
31         }while(low[i]!=dfn[i]);
32     }
33 }
34 int main(){
35     int n,m,i,j,a,_a,b,_b;
36     scanf("%d%d",&n,&m);
37     for(i=1;i<=m;i++){
38         scanf("%d%d%d%d",&a,&_a,&b,&_b);
39         if(_a==0&&_b==0)lian(a+n,b),lian(b+n,a);
40         else if(_a==1&&_b==0)lian(a,b),lian(b+n,a+n);
41         else if(_a==0&&_b==1)lian(a+n,b+n),lian(b,a);
42         else lian(a,b+n),lian(b,a+n);
43     }
44     for(i=1;i<=2*n;i++){
45         if(color[i]==0)tarjan(i);
46     }
47     for(i=1;i<=n;i++){
48         if(color[i]==color[i+n]){
49             printf("IMPOSSIBLE\n");
50             return 0;
51         }
52     }
53     printf("POSSIBLE\n");    
54     for(i=1;i<=n;i++){
55         if(color[i]<color[i+n])printf("0 ");
56         else printf("1 ");
57     }
58     return 0;
59 }

 

posted @ 2020-08-19 09:24  Jessica_Cao  阅读(246)  评论(0编辑  收藏  举报