2-SAT 做题笔记

但这篇写的很好啊 2-SAT学习笔记 - 万万没想到

例一:

P4782 【模板】2-SAT 问题

原题链接

P4782 【模板】2-SAT 问题

分析

这是一道2-SAT的模板题,要求xia或xjb,那么建i′->j和j′->i(i′代表与i相反)

1 for(int ii=1;ii<=m;ii++){
2     int i,a,j,b;
3     scanf("%d %d %d %d", &i, &a, &j, &b);
4     bu(i+n*!a,j+n*b);
5     bu(j+n*!b,i+n*a);
6 }

后面就是模板了

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=1000006;
 5 
 6 struct E{
 7     int ne,v;
 8 }e[N*2];
 9 
10 int head[N*2];
11 int tot=0;
12 void bu(int x,int y){
13     e[++tot].v=y;
14     e[tot].ne=head[x];
15     head[x]=tot;
16 }
17 
18 int dfn[N*2],low[N*2],st[N*2],scc[N*2];
19 int top=0,cnt=0,sccn=0;
20 void tarjan(int x){
21     dfn[x]=low[x]=++cnt;
22     st[++top]=x;
23     for(int i=head[x];i;i=e[i].ne){
24         int v=e[i].v;
25         if(!dfn[v]){
26             tarjan(v);
27             low[x]=min(low[x],low[v]); 
28         }
29         else if(!scc[v]) low[x]=min(low[x],dfn[v]);
30     }
31     if(dfn[x]==low[x]){
32         sccn++;
33         top++;
34         while(st[top--]!=x) scc[st[top]]=sccn;
35     }
36 }
37 
38 int main(){
39     int n,m;
40     scanf("%d %d",&n, &m);
41     for(int ii=1;ii<=m;ii++){
42         int i,a,j,b;
43         scanf("%d %d %d %d", &i, &a, &j, &b);
44         bu(i+n*!a,j+n*b);
45         bu(j+n*!b,i+n*a);
46     }
47     for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
48     for(int i=1;i<=n;i++) if(scc[i]==scc[i+n]) {cout<<"IMPOSSIBLE";return 0;}
49     cout<<"POSSIBLE"<<endl;
50     for(int i=1;i<=n;i++) if(scc[i]<scc[i+n]) cout<<0<<" "; else cout<<1<<" ";
51     return 0;
52 }

例二:

P4171 [JSOI2010] 满汉全席

原题链接

P4171 [JSOI2010] 满汉全席

分析

这道题建边与例一完全相同,但它的读入很恶心(鬼知道我卡了多久)

 upd:几几年了,还不用字符串读入呢。

 1 int a,b,i,j;
 2 char f=getchar();
 3 while(f!='m'&&f!='h')
 4     f=getchar();
 5 a=(f=='m');
 6 scanf("%d", &i);
 7 f=getchar();
 8 while(f!='m'&&f!='h')
 9     f=getchar();
10 b=(f=='m');
11 scanf("%d", &j);

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 struct E{
 5     int ne,v;
 6 }e[2005];
 7 
 8 int head[105*2];
 9 int tot=0;
10 void bu(int x,int y){
11     e[++tot].v=y;
12     e[tot].ne=head[x];
13     head[x]=tot;
14 }
15 
16 int dfn[105*2],low[105*2],st[105*2],scc[105*2];
17 int top=0,cnt=0,sccn=0;
18 void tarjan(int x){
19     dfn[x]=low[x]=++cnt;
20     st[++top]=x;
21     for(int i=head[x];i;i=e[i].ne){
22         int v=e[i].v;
23         if(!dfn[v]){
24             tarjan(v);
25             low[x]=min(low[x],low[v]); 
26         }
27         else if(!scc[v]) low[x]=min(low[x],dfn[v]);
28     }
29     if(dfn[x]==low[x]){
30         sccn++;
31         top++;
32         while(st[top--]!=x) scc[st[top]]=sccn;
33     }
34 }
35 
36 int main(){
37     int k;
38     scanf("%d", &k);
39     while(k--){
40         int n,m;
41         scanf("%d %d",&n, &m);
42         
43         memset(dfn,0,sizeof(dfn));
44         memset(low,0,sizeof(low));
45         memset(head,0,sizeof(head));
46         memset(scc,0,sizeof(scc));
47         memset(st,0,sizeof(st));
48         tot=0;
49         cnt=0;
50         sccn=0;
51         
52         for(int ii=1;ii<=m;ii++){
53             int a,b,i,j;
54             char f=getchar();
55             while(f!='m'&&f!='h')
56                 f=getchar();
57             a=(f=='m');
58             scanf("%d", &i);
59             f=getchar();
60             while(f!='m'&&f!='h')
61                 f=getchar();
62             b=(f=='m');
63             scanf("%d", &j);        
64             bu(i+n*!a,j+n*b);
65             bu(j+n*!b,i+n*a);
66         }
67         for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
68         bool f=false;
69         for(int i=1;i<=n;i++) if(scc[i]==scc[i+n]) {cout<<"BAD"<<endl;f=true;break;}
70         if(!f) cout<<"GOOD"<<endl;
71     }
72     return 0;
73 }

例三:

P3825 [NOI2017] 游戏

原题链接

P3825 [NOI2017] 游戏

分析

每种地图除去它不适合的赛车都有两种可能的赛车(除去x)那么这就是一个2-SAT问题了

x如何考虑呢?

观察数据范围可知x地图的数量不超过8个我们可以枚举每个x地图为a地图或c地图(任意两个即可)的情况,此时就包含A、B、C三种车型了。

建边时应考虑三种情况

如(i,hi,j,hj)

1.第i个地图不能使用hi:这条规则作废直接continue就好了

2.第i个地图能使用hi,第j个地图不能使用hj:建边i->i′表示不能在本次使用hi

3.第i个地图能使用hi,第j个地图能使用hj:建边i->j与j′->i′

代码

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int N=50005;
  5 const int M=100005;
  6 
  7 int n,d,m;
  8 int i1[M],j11[M],hi[M],hj[M];
  9 int tt=0;
 10 int xst[10];
 11 bool f=0;
 12 char S[N];
 13 
 14 int gx[5][5]={
 15 {-1,0,1},
 16 {0,-1,1},
 17 {0,1,-1}
 18 };
 19 
 20 struct E{
 21     int ne,v;
 22 }e[2*M];
 23 
 24 int head[2*N];
 25 int tot=0;
 26 void bu(int x,int y){
 27     e[++tot].v=y;
 28     e[tot].ne=head[x];
 29     head[x]=tot; 
 30 }
 31 
 32 int dfn[2*N],low[2*N],st[2*N],scc[2*N];
 33 int top=0,sccn=0,cnt=0;
 34 void tarjan(int x){
 35     dfn[x]=low[x]=++cnt;
 36     st[++top]=x;
 37     for(int i=head[x];i;i=e[i].ne){
 38         int v=e[i].v;
 39         if(!dfn[v]) {tarjan(v);low[x]=min(low[x],low[v]);}
 40         else if(!scc[v]) low[x]=min(low[x],dfn[v]); 
 41     }
 42     if(low[x]==dfn[x]){
 43         sccn++;top++;
 44         while(st[top--]!=x) scc[st[top]]=sccn;
 45     }
 46 } 
 47 
 48 void init(){
 49     memset(head,0,sizeof(head));
 50     memset(dfn,0,sizeof(dfn));
 51     memset(low,0,sizeof(low));
 52     memset(scc,0,sizeof(scc));
 53     memset(st,0,sizeof(st));
 54     for(int i=1;i<=tot;i++) {e[i].ne=0;e[i].v=0;}
 55     tot=cnt=top=sccn=0;    
 56 }
 57 
 58 bool work(){
 59     init();
 60     for(int i=1;i<=m;i++){
 61         int ic=i1[i],jc=j11[i],hic=hi[i],hjc=hj[i];
 62         int gxi=gx[S[ic]-'a'][hic],gxj=gx[S[jc]-'a'][hjc];
 63         if(gxi==-1) continue;
 64         else if(gxj==-1) bu(ic+n*gxi,ic+n*!gxi);
 65         else {bu(ic+n*gxi,jc+n*gxj);bu(jc+n*!gxj,ic+n*!gxi);}
 66     }
 67     for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
 68     for(int i=1;i<=n;i++) if(scc[i]==scc[i+n]) return 0;
 69     return 1;
 70 }
 71 
 72 void dfs(int now){
 73     if(now==d+1){
 74         if(work()) f=1;
 75         return;
 76     }
 77     S[xst[now]]='a';dfs(now+1);
 78     if(f) return;
 79     S[xst[now]]='c';dfs(now+1);
 80 }
 81 
 82 int main(){
 83     scanf("%d %d %s %d", &n, &d, S+1, &m);
 84     for(int i=1;i<=m;i++){
 85         char hic,hjc;
 86         cin>>i1[i]>>hic>>j11[i]>>hjc;
 87         hi[i]=hic-'A';
 88         hj[i]=hjc-'A';
 89     }
 90     for(int i=1;i<=n;i++) if(S[i]=='x') xst[++tt]=i;
 91     dfs(1);
 92     if(!f) cout<<-1;
 93     else{
 94         for(int i=1;i<=n;i++){
 95             if(scc[i]<scc[i+n]){
 96                 if(S[i]=='a') cout<<'B';
 97                 else cout<<'A';
 98             }
 99             else{
100                 if(S[i]=='c') cout<<'B';
101                 else cout<<'C';
102             }
103         }
104     }
105     return 0;
106 }

例四:

P5782 [POI2001] 和平委员会

原题链接

P5782 [POI2001] 和平委员会

分析

每个党派(i)都恰好有一个代表,那么则是从2*i-1与2*i中选择一个,这就是一个2-SAT问题了

代表a,b相互厌恶则建i->j′与j->i′

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=8005;
 5 const int M=20005;
 6 
 7 struct E{
 8     int ne,v;
 9 }e[2*M];
10 
11 int head[2*N];
12 int tot=0;
13 void bu(int x,int y){
14     e[++tot].v=y;
15     e[tot].ne=head[x];
16     head[x]=tot;
17 }
18 
19 int dfn[2*N],low[2*N],st[2*N],scc[2*N];
20 int top=0,cnt=0,sccn=0;
21 void tarjan(int x){
22     dfn[x]=low[x]=++cnt;
23     st[++top]=x;
24     for(int i=head[x];i;i=e[i].ne){
25         int v=e[i].v;
26         if(!dfn[v]) {tarjan(v);low[x]=min(low[x],low[v]);}
27         else if(!scc[v]) low[x]=min(low[x],dfn[v]);
28     }
29     if(dfn[x]==low[x]){
30         sccn++;top++;
31         while(st[top--]!=x) scc[st[top]]=sccn;
32     }
33 }
34 
35 int main(){
36     int n,m;
37     scanf("%d %d", &n, &m);
38     for(int ii=1;ii<=m;ii++){
39         int ac,bc;
40         int i,j,a,b;
41         scanf("%d %d", &ac, &bc);
42         i=(ac+1)/2;j=(bc+1)/2;
43         if(ac%2==1) a=0; else a=1;
44         if(bc%2==1) b=0; else b=1;
45         bu(i+n*a,j+n*!b);
46         bu(j+n*b,i+n*!a);
47     }
48     for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
49     for(int i=1;i<=n;i++) if(scc[i]==scc[i+n]) {cout<<"NIE";return 0;}
50     for(int i=1;i<=n;i++){
51         if(scc[i]<scc[i+n]) cout<<2*i-1<<endl;
52         else cout<<2*i<<endl;
53     }
54     return 0;
55 }

 练习题:

P3209 [HNOI2010] 平面图判定

P3513 [POI2011] KON-Conspiracy

P6378 [PA2010] Riddle

posted @ 2023-01-17 16:44  Idtwtei  阅读(26)  评论(0编辑  收藏  举报