2-SAT 做题笔记
但这篇写的很好啊 2-SAT学习笔记 - 万万没想到
例一:
P4782 【模板】2-SAT 问题
原题链接
分析
这是一道2-SAT的模板题,要求xi为a或xj为b,那么建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] 满汉全席
原题链接
分析
这道题建边与例一完全相同,但它的读入很恶心(鬼知道我卡了多久)
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] 游戏
原题链接
分析
每种地图除去它不适合的赛车都有两种可能的赛车(除去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] 和平委员会
原题链接
分析
每个党派(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 }