zoukankan      html  css  js  c++  java
  • 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
    ");
    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 }
  • 相关阅读:
    第6个作业
    团队-团队编程项目作业名称-团队一阶段互评
    课后作业-阅读任务-阅读提问-3
    20171106-构建之法:现代软件工程-阅读笔记》
    团队-团队编程项目作业名称-开发文档
    结对-结对编程项目作业名称-结对项目总结
    需求分析
    团队成员简介及分工
    课后作业-阅读任务-阅读提问-3
    结对编程项目作业5
  • 原文地址:https://www.cnblogs.com/Jessica-Cao/p/13527391.html
Copyright © 2011-2022 走看看