2-SAT
前置芝士:强连通分量。
先放一个板子题:2-SAT。
我们先考虑拆点,把每个变量 \(i\) 拆成两个点,\(i\times 2\) 和 \(i\times 2 + 1\),前一个代表这个变量 \(i\) 取假,后一个代表这个变量 \(i\) 取真。
既然有了点,我们就要考虑连边。例如给一个条件:\(1\) 取 \(1\) 或者 \(3\) 取 \(0\)。我们就要建两条边,一条为如果 \(1\) 取 \(0\) 则 \(3\) 取 \(0\),即 \(2\rightarrow 6\);另一条为如果 \(3\) 取 \(1\) 则 \(1\) 取 \(1\),即 \(7\rightarrow 3\)。
然后看一下这条边表示什么,表示的是一个要求是另一个要求的充分条件(建议学习一下充分条件与必要条件)。
那么,如果发现图中出现了 \(i\) 真是 \(i\) 假的充分条件;\(i\) 假是 \(i\) 真的充分条件,那么一定无解,因为不管 \(i\) 取什么,条件1总会要求你取 \(i\) 当前的值取反,这显然不可能成立。
换句话说,就是 \(\forall i\in[1,n]\) 如果 \(i\times 2\) 与 \(i\times 2 + 1\) 在一个强连通分量中,一定无解,否则有解。
说了这么多,我们终于说完了怎么判断有没有解,接下来说一下怎么构造解。
首先我们记 \(id_i\) 表示图中的 \(i\) 号点所在的强连通分量的编号。根据上面的描述,如果 \(\exists i\in [1,n],id_{i\times 2}=id_{i\times 2 + 1}\),无解。
否则这两个东西的 \(id\) 数组,一定一个大一个小(废话)。
那么,\(id\) 更小的那一部分可以认为是另一部分的上司(不同于寻常意义),换句话说它具有决定权,变量的值就取决于他。
这是一个小结论,证明的话建议看一些其他资料,这里建议记住,遇到的时候直接用就可以了。
下面放一下板子题的代码:
#include<bits/stdc++.h>
#define int long long
#define N 2000005
#define M 2000005
using namespace std;
int n,m,h[N],e[M],ne[M],idx,dfn[N],low[N],ts,stk[N],top,id[N],cnt;
bool ins[N];
void add(int a,int b){//链式前向星建边
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void tar(int u){//经典tarjan求强连通分量
dfn[u]=low[u]=++ts;
stk[++top]=u;
ins[u]=1;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!dfn[j]){
tar(j);
low[u]=min(low[u],low[j]);
}
else if(ins[j])low[u]=min(low[u],dfn[j]);
}
if(low[u]==dfn[u]){
int y;
cnt++;
do{
y=stk[top--];
ins[y]=0;
id[y]=cnt;
}while(y!=u);
}
}
signed main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int i,a,j,b;
cin>>i>>a>>j>>b;
i--;j--;
add(2*i+!a,2*j+b);//按照上面说的充分条件的方式建边
add(2*j+!b,2*i+a);
}
for(int i=0;i<n*2;i++){
if(!dfn[i]){//tarjan
tar(i);
}
}
for(int i=0;i<n;i++){
if(id[i*2]==id[i*2+1]){//互为充分条件则无解
cout<<"IMPOSSIBLE";
return 0;
}
}
cout<<"POSSIBLE\n";//否则有解
for(int i=0;i<n;i++){
if(id[i*2]<id[i*2+1]){//"上司"具有最终决定权
cout<<"1 ";
}
else cout<<"0 ";
}
return 0;
}
再来一个稍微困难的题:满汉全席。
先把原题面进行转化,发现对于每个评审员要满足两个要求的至少一个。所以可以认为 \(m\) 代表假,\(h\) 代表真,这就转化成了上一道板子题。
我们对 \(m\) 和 \(h\) 进行分类讨论,每个要求建 \(2\) 条边,最后判断代表一个变量的两个点在不在一个强连通分量中即可。
这里把这道题进行略微强化,请读者思考如果 \(2\) 个要求能且仅能满足 \(1\) 个该如何做。