2-SAT学习笔记

2-SAT学习笔记

1.what is:

   有一个由n个bool变量组成的序列A,有一定的限制条件,求出满足条件的方案。

2.how to do:

 (1).建图:

   将每个点拆成(i<<1)和((i<<1)|1)[即 2*i 和 2*i+1 ],分别代表这个变量是真或假。

   连接有向边(u->v)表示由u定能推出v。

   有几种条件:

   <1>.x or y(x|y):

      当x为假时,y一定为真;当y为假时,x一定为真。

      所以,连接(x<<1)->((y<<1)|1),(y<<1)->((x<<1)|1)。

   <2>.x or(not y)(x|(!y)):

 

      当x为假时,y一定为假;当y为真时,x一定为真。

 

      所以,连接(x<<1)->(y<<1),((y<<1)|1)->((x<<1)|1)。

   <3>.x xor y(x^y):

      当x为假时,y一定为真;当y为假时,x一定为真;

      当x为真时,y一定为假;当y为真时,x一定为假;

      所以,连接(x<<1)->((y<<1)|1),(y<<1)->((x<<1)|1),

                ((x<<1)|1)->(y<<1),((y<<1)|1)->(x<<1)。

       <4>.x xor (not y)(x^(!y)):

      当x为假时,y一定为假;当y为假时,x一定为假;

      当x为真时,y一定为真;当y为真时,x一定为真;

      所以,连接(x<<1)->(y<<1),(y<<1)->(x<<1),

                ((x<<1)|1)->((y<<1)|1),((y<<1)|1)->((x<<1)|1)。

 (2).判断:

   对于一个强连通分量,选其中一个就能推出其它。

   所以如果(i<<1)和((i<<1)|1)处在一个强连通分量中,此题无解。

   若(i<<1)的强连通分量能推出((i<<1)|1)的强连通分量,选(i<<1)则必选((i<<1)|1),矛盾;所以只能选((i<<1)|1)。

   若((i<<1)|1)的强连通分量能推出(i<<1)的强连通分量,反之,只能选(i<<1)。

   若(i<<1)的强连通分量和((i<<1)|1)的强连通分量不存在推断关系,任意选择皆可。

   所以直接按拓扑序排。

   如果(i<<1)的拓扑序大于((i<<1)|1)的拓扑序,选(i<<1);

   否则,选((i<<1)|1)。 

   又因tarjan是按dfs序搜索,所以缩点后的编号是反的拓扑序。

   直接按编号比较即可:

   如果(i<<1)的拓扑序小(不是“大”)于((i<<1)|1)的拓扑序,选(i<<1);

 

   否则,选((i<<1)|1)。

 

   有人可能会问,“不妨选”的情况难道不会导致错乱?

   同级都可选择的(i<<1)和((i<<1)|1)判断是否是玄学。

   存不存在都可选择的两点u和v,u选为假,(u<<1)->((v<<1)|1),v却也选为假。

   

   图中,模拟tarjan过程如下:

    1>.2:缩点为1;

    2>.6、7、8:缩点为2;

    3>.5:缩点为3;

    4>.3:缩点为4;

 

    5>.9、10、11:缩点为5;

 

    6>.4:缩点为6;

   2、3为1的真和假,4、5为2的真和假。

   编号:2<3,5<4;

   选择2和5,处于同一链上。

   即tarjan会先搜处于同一链上的点所以处于同一链上的点都是大于或小于另一条链的,不存在一个大一个小的情况。

   若u选为假,则编号(u<<1)<((u<<1)|1),(u<<1)->((v<<1)|1),则编号((v<<1)|1)<(v<<1)。

 (3).代码:

  这里,假为i,真为(i+n)。

 1 #include<bits/stdc++.h>
 2 #define re register
 3 using namespace std;
 4 const int N=2000006;
 5 int head[N],cnt=0,n,m,t1,t2,dfn[N],low[N],num[N],s[N],tot=0,top=0,deep=0,t3,t4;
 6 struct edge
 7 {
 8     int nxt,to;
 9 }e[N];
10 inline int read()
11 {
12     int T=0,F=1; char ch=getchar();
13     while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
14     while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
15     return F*T;
16 }
17 inline void add(int u,int v){e[++cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt;} 
18 void tarjan(int u)
19 {
20     dfn[u]=low[u]=++deep; s[++top]=u;
21     for(int i=head[u];i;i=e[i].nxt)
22     {
23         if(!dfn[e[i].to]) tarjan(e[i].to),low[u]=min(low[u],low[e[i].to]);
24         else if(!num[e[i].to]) low[u]=min(low[u],dfn[e[i].to]);
25     }
26     if(dfn[u]==low[u])
27     {
28         ++tot; num[u]=tot;
29         while(s[top]!=u) num[s[top]]=tot,--top;
30         --top;
31     }
32 }
33 int main()
34 {
35     n=read(),m=read();
36     for(re int i=1;i<=m;++i) t1=read(),t2=read(),t3=read(),t4=read(),add(t1+(t2^1)*n,t3+t4*n),add(t3+(t4^1)*n,t1+t2*n); cnt=0;
37     //建图 
38     n<<=1;
39     for(re int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); memset(dfn,0,sizeof(dfn));
40     //缩点 
41     n>>=1; deep=0;
42     for(re int i=1;i<=n;++i) if(num[i]==num[i+n]){printf("IMPOSSIBLE"); return 0;}
43     //是否真假在同一强连通分量 
44     printf("POSSIBLE\n");    
45     for(re int i=1;i<=n;++i)
46     {
47         if(num[i]<num[i+n]) printf("0 ");
48         else printf("1 ");
49     }
50     return 0;
51 }

 

posted @ 2019-05-27 22:12  lsoi_ljk123  阅读(177)  评论(0编辑  收藏  举报