推荐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 "); 50 return 0; 51 } 52 } 53 printf("POSSIBLE "); 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 }