【2-SAT】学习小结

  这两天学习了一下2-SAT,主要参考了以下两个资料:

     《由对称性解2-SAT问题》

     《2-SAT解法浅析》

  另外还有这个博客我觉得讲得挺白话的。

建图:

  2-SAT问题远没有网络流那样复杂,只要抓住关系建好图基本就直接可以解了,在这类问题中建边的规则就是“必须”,对于边<i,j>,它的意义就是选择了i就必须选j。

  对于题目中给出的每对关系都可以化成下面的几种形式:

   A,B不能同时取  <A,B'><B,A'>

   A,B不能都不取  <A',B><B',A>

   A,B必须都取或者都不取  <A,B><B,A><A',B'><B',A'>

   必须取A  <A',A>

  主要的模型就这四个,其余的都可以通过徳摩根律化成上面的形式,前三个的建图都不难理解,对于第四个,这样建边是因为如果选了A'就必须选A,显然是不符合逻辑的,也就是说A'不可选,那么自然要选择A了。

 

算法

  两种解法,第一种就是在图上直接暴力,递归+回溯来找可行解,时间复杂度O(n*m),这个算法比较慢,但是也不是毫无用武之地,如果要求字典序最小或者什么别的特殊要求的解的话,那么只能这样暴力枚举了。

  如果仅仅是构造一个可行解的话,那么用论文里给的办法就行,也就是强连通缩点,然后判不相容的两个点是不是在一个连通分支。然后对新图边取反拓扑排序,每次把未染色的连通分支及和它对立的连通分支染色。至于为什么这么做,我还没有彻底明白,有待思考

入门练习

  2-SAT本身就是个判定性问题,最基本的题型就是给定一些关系然后判定是否可解什么的,当然很多时候还会套上别的东西,一般的就是二分枚举答案+2-SAT判定是否有解。

  还有一类就是在判定可解性之后,构造一个可行性方案。

pku 3207

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #define N 1010
 6 using namespace std;
 7 struct Edge{
 8     int u,v,next;
 9     Edge(){}
10     Edge(int _u,int _v,int _next){
11         u=_u;v=_v;next=_next;
12     }
13 }edge[N*N];
14 int dfn[N],low[N],id[N],stack[N*N],cc;
15 int head[N],cnt,dep,top;
16 int n,m;
17 bool instack[N];
18 void init(){
19     memset(head,-1,sizeof(head));
20     memset(instack,0,sizeof(instack));
21     cnt=0;
22 }
23 void addedge(int u,int v){
24     edge[cnt]=Edge(u,v,head[u]);head[u]=cnt++;
25 }
26 void tarjan(int u){
27     int v;
28     dfn[u]=low[u]=++dep;
29     stack[++top]=u;
30     instack[u]=1;
31     for(int k=head[u];k!=-1;k=edge[k].next){
32         v=edge[k].v;
33         if(!dfn[v]){
34             tarjan(v);
35             low[u]=min(low[u],low[v]);
36         }else if(instack[v])
37             low[u]=min(low[u],dfn[v]);
38     }
39     if(dfn[u]==low[u]){
40         ++cc;
41         do{
42             v=stack[top--];
43             instack[v]=0;
44             id[v]=cc;
45         }while(v!=u);
46     }
47 }
48 void solve(){
49     dep=cc=top=0;
50     memset(dfn,0,sizeof(dfn));
51     for(int i=1;i<=2*m;i++)
52         if(!dfn[i])tarjan(i);
53 }
54 int seg[N][2];
55 bool check(int i,int j){
56     return seg[i][0]<seg[j][0]&&seg[j][0]<seg[i][1]&&seg[i][1]<seg[j][1];
57 }
58 int main(){
59     while(~scanf("%d%d",&n,&m)){
60         for(int i=1;i<=m;i++)
61             scanf("%d%d",&seg[i][0],&seg[i][1]);
62         init();
63         for(int i=1;i<=m;i++)
64             for(int j=i+1;j<=n;j++)
65                 if(check(i,j)||check(j,i)){
66                     addedge(i,i+m);
67                     addedge(j,i+m);
68                     addedge(i+m,j);
69                     addedge(j+m,i);
70                 }
71         solve();
72         bool flag=1;
73         for(int i=1;i<=m;i++){
74             if(id[i]==id[i+m]){
75                 flag=0;
76                 break;
77             }
78         }
79         if(flag)
80             puts("panda is telling the truth...");
81         else
82             puts("the evil panda is lying again");
83     }
84     return 0;
85 }

 

pku 3683

 

pku 3683

 

pku 3678

pku3678

 

pku 3648

pku3648
  1 /*
  2     建边规则:<x,y>表示如果x在新娘对面,那么y必须也在新娘对面。
  3     最后在新娘新郎之家建一条边表示新郎必须和新娘相对。
  4     输出方案的时候要判断该点颜色是否和新娘的一致。
  5 */
  6 
  7 #include<cstdio>
  8 #include<cstring>
  9 #include<algorithm>
 10 #include<vector>
 11 #include<queue>
 12 #define N 110
 13 using namespace std;
 14 struct Edge{
 15     int u,v,next;
 16     Edge(){}
 17     Edge(int _u,int _v,int _next){
 18         u=_u;v=_v;next=_next;
 19     }
 20 }edge[N*N];
 21 int head[N],cnt;
 22 int dfn[N],low[N],stack[N],id[N],num,dep,top;
 23 bool instack[N];
 24 vector<int>V[N];
 25 int c[N],deg[N];
 26 void init(){
 27     memset(head,-1,sizeof(head));
 28     cnt=0;
 29 }
 30 void add(int u,int v){
 31     edge[cnt]=Edge(u,v,head[u]);head[u]=cnt++;
 32 }
 33 void tarjan(int u){
 34     int v;
 35     dfn[u]=low[u]=++dep;
 36     stack[++top]=u;
 37     instack[u]=1;
 38     for(int k=head[u];k!=-1;k=edge[k].next){
 39         v=edge[k].v;
 40         if(!dfn[v]){
 41             tarjan(v);
 42             low[u]=min(low[u],low[v]);
 43         }else if(instack[v]){
 44             low[u]=min(low[u],dfn[v]);
 45         }
 46     }
 47     if(dfn[u]==low[u]){
 48         ++num;
 49         do{
 50             v=stack[top--];
 51             instack[v]=0;
 52             id[v]=num;
 53         }while(u!=v);
 54         deg[num]=0;
 55         V[num].clear();
 56     }
 57 }
 58 int n;
 59 int x[N];
 60 int opp(int x){
 61     return x<n?(x+n):(x-n);
 62 }
 63 bool solve(){
 64     memset(dfn,0,sizeof(dfn));
 65     memset(instack,0,sizeof(instack));
 66     dep=num=top=0;
 67     for(int i=0;i<n*2;i++)
 68         if(!dfn[i])tarjan(i);
 69     for(int i=0;i<n;i++){
 70         if(id[i]==id[i+n])return 0;
 71         x[id[i]]=id[i+n];
 72         x[id[i+n]]=id[i];
 73     }
 74     return 1;
 75 }
 76 void topo(){
 77     queue<int>Q;
 78     for(int i=0;i<2*n;i++)
 79         for(int k=head[i];k!=-1;k=edge[k].next){
 80             int v=edge[k].v;
 81             if(id[i]!=id[v]){
 82                 V[id[v]].push_back(id[i]);
 83                 deg[id[i]]++;
 84             }
 85         }
 86     memset(c,0,sizeof(c));
 87     for(int i=1;i<=num;i++)
 88         if(deg[i]==0)Q.push(i);
 89     while(!Q.empty()){
 90         int p=Q.front();Q.pop();
 91         if(c[p]==0){
 92             c[p]=1;
 93             c[x[p]]=-1;
 94         }
 95         for(int i=0;i<V[p].size();i++){
 96             --deg[V[p][i]];
 97             if(deg[V[p][i]]==0)Q.push(V[p][i]);
 98         }
 99     }
100 }
101 int main(){
102     int m,x,y;
103     char a,b;
104     while(~scanf("%d%d",&n,&m)){
105         if(!(n||m))return 0;
106         init();
107         for(int i=1;i<=m;i++){
108             scanf("%d%c %d%c",&x,&a,&y,&b);
109             if(a=='h')x+=n;
110             if(b=='h')y+=n;
111             add(x,opp(y));
112             add(y,opp(x));
113         }
114         add(0,opp(0));
115         if(!solve()){
116             puts("bad luck");
117             continue;
118         }
119         topo();
120         bool flag=0;
121         for(int i=1;i<n;i++){
122             if(flag)printf(" ");
123             flag=1;
124             if(c[id[i]]==c[id[0]])
125                 printf("%dw",i);
126             else
127                 printf("%dh",i);
128         }
129         puts("");
130     }
131     return 0;
132 }

 

pku 2723

pku2723

 

pku 2749

pku2749

 

pku 3905

 

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define N 2010
 5 using namespace std;
 6 struct Edge{
 7     int u,v,next;
 8     Edge(){}
 9     Edge(int _u,int _v,int _next){
10         u=_u;v=_v;next=_next;
11     }
12 }edge[N*N];
13 int head[N],cnt;
14 int dfn[N],low[N],stack[N],id[N],num,dep,top;
15 bool instack[N];
16 void init(){
17     memset(head,-1,sizeof(head));
18     cnt=0;
19 }
20 void add(int u,int v){
21     edge[cnt]=Edge(u,v,head[u]);head[u]=cnt++;
22 }
23 void tarjan(int u){
24     int v;
25     dfn[u]=low[u]=++dep;
26     stack[++top]=u;
27     instack[u]=1;
28     for(int k=head[u];k!=-1;k=edge[k].next){
29         v=edge[k].v;
30         if(!dfn[v]){
31             tarjan(v);
32             low[u]=min(low[u],low[v]);
33         }else if(instack[v]){
34             low[u]=min(low[u],dfn[v]);
35         }
36     }
37     if(dfn[u]==low[u]){
38         ++num;
39         do{
40             v=stack[top--];
41             instack[v]=0;
42             id[v]=num;
43         }while(u!=v);
44     }
45 }
46 int n;
47 bool solve(){
48     memset(dfn,0,sizeof(dfn));
49     memset(instack,0,sizeof(instack));
50     num=dep=top=0;
51     for(int i=1;i<=2*n;i++)
52         if(!dfn[i])tarjan(i);
53     for(int i=1;i<=n;i++)
54         if(id[i]==id[i+n])return 0;
55     return 1;
56 }
57 int main(){
58     int m;
59     int x,y;
60     char a[3],b[3];
61     while(~scanf("%d%d",&n,&m)){
62         init();
63         for(int i=1;i<=m;i++){
64             scanf("%1s%d%1s%d",a,&x,b,&y);
65             if(a[0]=='+'){
66                 if(b[0]=='+'){
67                     add(x+n,y);add(y+n,x);
68                 }else{
69                     add(x+n,y+n);add(y,x);
70                 }
71             }else{
72                 if(b[0]=='+'){
73                     add(x,y);add(y+n,x+n);
74                 }else{
75                     add(x,y+n);add(y,x+n);
76                 }
77             }
78         }
79         printf("%d\n",solve());
80     }
81     return 0;
82 }

 

 hdu 1814

求字典序最小的解,暴力。。。

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define  N 20010
 5 using namespace std;
 6 struct Edge{
 7     int u,v,next;
 8     Edge(){}
 9     Edge(int _u,int _v,int _next){
10         u=_u;v=_v;next=_next;
11     }
12 }edge[N*10];
13 int head[N],cnt;
14 int dfn[N],low[N],stack[N],id[N],num,dep,top;
15 bool instack[N];
16 void init(){
17     memset(head,-1,sizeof(head));
18     cnt=0;
19 }
20 void add(int u,int v){
21     edge[cnt]=Edge(u,v,head[u]);head[u]=cnt++;
22 }
23 int c[N],s[N],cc;
24 int n,m;
25 int opp(int x){
26     if(x&1)return x+1;
27     else return x-1;
28 }
29 bool dfs(int u){
30     if(c[u]==1)return 1;
31     if(c[u]==-1)return 0;
32     c[u]=1;
33     c[opp(u)]=-1;
34     s[++cc]=u;
35     for(int k=head[u];k!=-1;k=edge[k].next){
36         int v=edge[k].v;
37         if(!dfs(v))return 0;
38     }
39     return 1;
40 }
41 int main(){
42     int a,b;
43     while(~scanf("%d%d",&n,&m)){
44         init();
45         for(int i=1;i<=m;i++){
46             scanf("%d%d",&a,&b);
47             add(a,opp(b));
48             add(b,opp(a));
49         }
50         memset(c,0,sizeof(c));
51         bool flag=1;
52         for(int i=1;i<=2*n&&flag;i++){
53             if(c[i]!=0)continue;
54             cc=0;
55             if(!dfs(i)){
56                 for(int j=1;j<=cc;j++){
57                     c[s[j]]=0;
58                     c[opp(s[j])]=0;
59                 }
60                 if(!dfs(opp(i)))flag=0;
61             }
62         }
63         if(!flag){
64             puts("NIE");
65             continue;
66         }
67         for(int i=1;i<=2*n;i++)
68             if(c[i]==1)printf("%d\n",i);
69     }
70     return 0;
71 }

 

其他的问题以后遇到了再更新。。。

  

 

    

posted @ 2013-02-18 01:24  silver__bullet  阅读(4848)  评论(2编辑  收藏  举报