定义
简略的说, n n n 个变量 a i a_i ai,且 a i ∈ [ 0 , 1 ] a_i\in[0,1] ai∈[0,1]。
给出一些条件,形式如下:
a i a_i ai o p op op a j = 0 / 1 ( o p ∈ [ a n d , o r , x o r ] ) a_j =0/1(op\in[and,or,xor]) aj=0/1(op∈[and,or,xor])
求解 2 − S A T 2-SAT 2−SAT 即找到一组合法的 a a a 满足所有限制。
图论建图
明确问题后,尝试用图论方法去解决。
考虑到一个变量 a i a_i ai 只有 0 / 1 0/1 0/1 两种取值,尝试将一个点 a i a_i ai 进行拆点 P [ 2 ∗ i ] , P [ 2 ∗ i + 1 ] P[2*i],P[2*i+1] P[2∗i],P[2∗i+1],分别表示为将 a i a_i ai 取为 0 0 0 和 1 1 1 两种情况。
定义有向边 x → y x\rightarrow y x→y 为选择了 x x x 就必须要选择 y y y ,举个栗子表示一下。
- a i a_{i} ai x o r xor xor a j = 1 a_j=1 aj=1 ,即 i → j ‾ i\rightarrow \overline{j} i→j, j → i ‾ j\rightarrow \overline{i} j→i
- a i a_{i} ai x o r xor xor a j = 0 a_j=0 aj=0 ,即 i → j i\rightarrow j i→j, j → i j\rightarrow i j→i
- a i a_{i} ai o r or or a j = 1 a_j=1 aj=1 ,即 i → j ‾ i\rightarrow \overline{j} i→j, j → i ‾ j\rightarrow \overline{i} j→i , i ‾ → j \overline{i} \rightarrow j i→j, j ‾ → i \overline{j} \rightarrow i j→i
- a i = 1 a_i=1 ai=1 ,即 i ‾ → i \overline{i} \rightarrow i i→i
求解
一、暴力DFS(常用于求最小字典序) ( O ( n ( m + n ) ) ) (O(n(m+n))) (O(n(m+n)))
遍历每个点,如果当前点 a i a_i ai 未确定,则:
- 令 a i = 0 a_i=0 ai=0 ,由该点出发遍历其相关的点。
- 若出现任一 j j j 使得 a j = 0 a_j=0 aj=0 和 a j = 1 a_j=1 aj=1 同时取到的情况,则返回 f a l s e false false
- 若出现第 2 2 2 步中的情况,则从 a i = 1 a_i=1 ai=1 出发进行遍历
- 若第 3 3 3 步也无法完成,则返回无解
若对于所有的 a i a_i ai 均没有出现无解的情况,则可找到答案。
二、SCC缩点
不了解SCC是什么东西的我就emmm。
将所有的点进行缩点,在一个强连通分量内的点是要同时被选中的,如果 a i = 0 a_i=0 ai=0 和 a i = 1 a_i=1 ai=1 的点在一个SCC中,那么明显无解,这个比暴搜快的多。
在缩点完成后,我们只要选择 a i = 0 a_i=0 ai=0 和 a i = 1 a_i=1 ai=1 中拓扑序较大的点即可,至于为什么就不解释了吧。。
例题
缩点模板
题目链接: https://www.luogu.org/problem/P4782
题意:
有n个布尔变量
x
1
x_{1}
x1 到
x
n
x_{n}
xn 另有
m
m
m 个需要满足的条件,每个条件的形式都是"
x
i
=
t
r
u
e
/
f
a
l
s
e
x_{i}=true/false
xi=true/false " 或者 "
x
j
=
t
r
u
e
/
f
a
l
s
e
x_{j}=true/false
xj=true/false " 。 比如“
x
1
x_{1}
x1 为真或
x
3
x_{3}
x3 为假 ”。2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
const int maxn=2000005;
const int maxm=2000005;
const int inf=(int) 2e9+7;
int fr[maxm],to[maxm];
int head[maxn],sta[maxn],top,now;
int belong[maxn],qiang,dfn[maxn],cnt;
int n,m,ans[maxn],nex[maxm],low[maxn];
void add(int u,int v){
to[now]=v;
nex[now]=head[u]; head[u]=now++;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt;
sta[++top]=x;
for(int i=head[x];~i;i=nex[i]){
int v=to[i];
if(!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(!belong[v]) low[x]=min(low[x],low[v]);
}
if(dfn[x]==low[x]){
qiang++;
while(1){
int u=sta[top--];
belong[u]=qiang;
if(u==x) break;
}
}
}
int id(int x,int f){
if(f) return x+n;
return x;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
rep(i,1,m){
int a,b,f1,f2;
scanf("%d%d%d%d",&a,&f1,&b,&f2);
add(id(a,f1^1),id(b,f2));
add(id(b,f2^1),id(a,f1));
}
rep(i,1,2*n) if(!dfn[i]) tarjan(i);
rep(i,1,n){
if(belong[i]==belong[i+n]) return 0*printf("IMPOSSIBLE\n");
if(belong[i]>belong[i+n]) ans[i]=1;
}
printf("POSSIBLE\n");
rep(i,1,n) printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}
2018-2019 ACM-ICPC, Asia Seoul Regional Contest K.TV Show Game
题目链接: https://codeforces.com/gym/101987
题意:
数组 a a a 有 n n n 个值 a [ i ] ∈ [ 0 , 1 ] a[i]\in[0,1] a[i]∈[0,1],现在有 m m m 个条件,为 ( ¬ ) a x + ( ¬ ) a y + ( ¬ ) a x > = 2 (\neg)a_x+(\neg) a_y+(\neg)a_x>=2 (¬)ax+(¬)ay+(¬)ax>=2 ,现在问你是否存在一个合法的数组 a a a 满足所有条件。
做法:
因为三个条件里面至少有两个条件要求满足,所以如果某一条件中为 a x a_x ax,那么如果满足 ¬ a x \neg a_x ¬ax ,那么其他两个都要满足要求的值,按照这个条件建边直接跑2-SAT 板子就好了。
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep_e(i,u,v) for(int i=head[u],v=to[i];~i;i=nex[i],v=to[i])
using namespace std;
typedef long long ll;
const int maxn=20005;
const int maxm=10005*8;
int belong[maxn],qiang,dfn[maxn],now;
int n,m,ans[maxn],low[maxn],sta[maxn],top;
int head[maxn],to[maxm],nex[maxm],cnt;
void add(int u,int v){
to[cnt]=v;nex[cnt]=head[u];
head[u]=cnt++;
}
void tarjan(int x){
dfn[x]=low[x]=++now;
sta[++top]=x;
for(int i=head[x];~i;i=nex[i]){
int v=to[i];
if(!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(!belong[v]) low[x]=min(low[x],low[v]);
}
if(dfn[x]==low[x]){
qiang++;
while(1){
int u=sta[top--];
belong[u]=qiang;
if(u==x) break;
}
}
}
int id(int x,int f){
if(f) return x+n;
return x;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
rep(i,1,m){
int a,b,c,d,e,f; char x[5],y[5],z[5];
scanf("%d%s%d%s%d%s",&a,x,&b,y,&c,z);
d=(x[0]=='R'?1:0);
e=(y[0]=='R'?1:0);
f=(z[0]=='R'?1:0);
add(id(a,d^1),id(b,e));
add(id(a,d^1),id(c,f));
add(id(b,e^1),id(a,d));
add(id(b,e^1),id(c,f));
add(id(c,f^1),id(a,d));
add(id(c,f^1),id(b,e));
}
rep(i,1,2*n){
if(!dfn[i]) tarjan(i);
}
int flag=0;
rep(i,1,n){
if(belong[i]==belong[i+n]) return 0*printf("-1\n");
if(belong[i]<belong[i+n]) ans[i]=1;
}
rep(i,1,n){
printf("%c",(ans[i]==1?'B':'R'));
}
printf("\n");
return 0;
}