西安多校集训-2-SAT
2-SAT问题
有 n 个变量,每一个变量都是 bool 类型的,除了这 n 个变量以外,我们还有 m 个关系表达式,关系表达式差不多是这样的:
$x_1 \text{&} x_2 = false $(注意每个表达式只会有两个变量)
问给出 m 个关系表达式后,能否给这 n 个变量找出一个赋值的方法,使得满足所有的表达式。
——来自洛谷题解区大佬
具体做法
有点像种类并查集?
如果我们令变量 \(i\) 表示 \(i\) 的状态 1 ,令\(i+n\) 表示 \(i\) 的状态 2 (只能有两种状态,且不能共存)。
那么如果此时有一变量 \(j\) 不能与 \(i\) 共存,那么令 \(j\) 向 \(i+n\) 连边,同时 \(j+n\) 向 \(i\) 连边。反之亦然。
然后直接 Tarjan 缩点,再根据题意做即可。
例题
【模板】2-SAT
如上述直接做即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m;
vector<int> vec[2*N];
int dfn[2*N],low[2*N],dfx;
int sta[2*N],t;
bool vis[2*N];
int scc=0,color[2*N];
void tarjan(int u){
dfn[u]=low[u]=++dfx;
sta[++t]=u;
vis[u]=1;
for(auto v:vec[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else{
if(vis[v]) low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
scc++;
while(sta[t]!=u){
color[sta[t]]=scc;
vis[sta[t]]=0;
t--;
}
vis[u]=0;
color[u]=scc;
t--;
}
return ;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,a,y,b;
cin>>x>>a>>y>>b;
vec[x+n*(a&1)].push_back(y+n*(b^1));
vec[y+n*(b&1)].push_back(x+n*(a^1));
}
for(int i=1;i<=2*n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
if(color[i]==color[i+n]){
cout<<"IMPOSSIBLE";
return 0;
}
}
cout<<"POSSIBLE\n";
for(int i=1;i<=n;i++){
if(color[i]<color[i+n]) cout<<"1 ";
else cout<<"0 ";
}
return 0;
}
因为 Tarjan 缩点得到的拓扑序是反的,所以最后输出时是小于号,如果你用别的,就是大于号。
和平委员会
怎么感觉比板子还简单呢。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m;
vector<int> vec[4*N];
int get(int x){
if(x%2==0) return x-1;
else return x+1;
}
int dfn[N],low[N],dfx,sta[N],top;
int scc,color[N];
bool vis[N];
void tarjan(int u){
dfn[u]=low[u]=++dfx;
sta[++top]=u;
vis[u]=1;
for(auto v:vec[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else{
if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
}
if(low[u]==dfn[u]){
scc++;
while(sta[top]!=u){
vis[sta[top]]=0;
color[sta[top]]=scc;
top--;
}
vis[u]=0;
color[u]=scc;
top--;
}
return ;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
vec[u].push_back(get(v));
vec[v].push_back(get(u));
}
for(int i=1;i<=2*n;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=2*n;i+=2){
if(color[i]==color[i+1]){
cout<<"NIE";
return 0;
}
}
for(int i=1;i<=2*n;i+=2){
if(color[i]<color[i+1]){
cout<<i<<'\n';
}else{
cout<<i+1<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号