2-SAT 学习笔记
jiangjiangQwQ · · 算法·理论
优质博客
panyf的博客
基础模型
给出若干个 bool 型的限制条件:若
解决思路
考虑将限制条件都转换成图论中的连边,对于若
如果拆点建图之后同一点会产生矛盾的两个状态点在同一个 SCC 中,显然不合法。因此我们可以称其中一个为合法点,另一个为不合法点,那么如果有非法点
那如果原图不是 DAG 呢,直接上缩点就好了嘛。每一个强连通分量都会成为一个点,只要强连通分量其中一个点合法,则其它点必然都合法。那么在新图中,一个点的合法点所在的强连通分量的编号小于非法点。可以有这种方式理解:如果跑完 Tarjan 缩点之后呈现出的拓扑序更大,在 Tarjan 会更晚被遍历到,就会更早地被弹出栈而缩点,编号会更小。
接下来为判断是否存在合法解,如果同一点的两个矛盾的状态点存在于同一个强连通分量中,那么整个问题无解。如果两个点之间不存在任何路径,即使有有向边,那么选择 SCC 编号小的点就好了。
例题探究
1.模板\ Code:
#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
#define int long long
#define re register
#define For(i,l,r) for(re int i=l;i<=r;i++)
#define Rep(i,l,r) for(re int i=l;i>=r;i--)
#define ls(c) c<<1
#define rs(c) c<<1|1
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
const int N=3e6+5;
inline void fast(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
}
inline void read(int &x){
x=0;int f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;
c=getchar();
}while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}x*=f;
}
inline void write(int x){
if(x<0){x=-x;putchar('-');}
if(x>9) write(x/10);
putchar(x%10+'0');
}int n,m,i,a,j,b;
vector<int> e[N];
int dfn[N],low[N],s[N],tp,ins[N];
int sc,scc[N],dfncnt;
inline void tarjan(int u){
dfn[u]=low[u]=++dfncnt;
s[++tp]=u;ins[u]=1;
for(int j=0;j<e[u].size();j++){
int v=e[u][j];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(ins[v]){
low[u]=min(low[u],dfn[v]);
}
}if(dfn[u]==low[u]){
++sc;
while(s[tp]!=u){
ins[s[tp]]=0;
scc[s[tp]]=sc;
--tp;
}
ins[s[tp]]=0;
scc[s[tp]]=sc;
--tp;
}
}
signed main(){
fast();
cin>>n>>m;
For(k,1,m){
cin>>i>>a>>j>>b;
//+n=1
if(a&&b){
e[i].push_back(j+n);
e[j].push_back(i+n);
}else if(a&&!b){
e[i].push_back(j);
e[j+n].push_back(i+n);
}else if(!a&&b){
e[i+n].push_back(j+n);
e[j].push_back(i);
}else if(!a&&!b){
e[i+n].push_back(j);
e[j+n].push_back(i);
}
}
For(k,1,n*2){
if(!dfn[k]) tarjan(k);
}
For(k,1,n){
if(scc[k]==scc[k+n]){
cout<<"IMPOSSIBLE\n";
return 0;
}
}
cout<<"POSSIBLE\n";
For(k,1,n){
cout<<(scc[k]>scc[k+n])<<' ';
}
return 0;
}
2.简单题(套模板) 直接根据题目条件建边即可。\ Code:
#include <iostream>
#include <queue>
#include <set>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define int long long
template<typename type>
inline void read(type &x)
{
x=0;bool flag(0);char ch=getchar();
while(!isdigit(ch)) flag^=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
flag?x=-x:0;
}
template<typename type>
inline void write(type x)
{
x<0?x=-x,putchar('-'):0;
static short Stack[50],top(0);
do Stack[++top]=x%10,x/=10;while(x);
while(top) putchar(Stack[top--]|48);
}
inline char read(char &ch){return ch=getchar();}
inline char write(const char &ch){return putchar(ch);}
template<typename type,typename ...T>
inline void read(type &x,T&...y){read(x),read(y...);}
template<typename type,typename ...T>
inline void write(type x,T...y){write(x),putchar(' '),write(y...),sizeof...(y)^1?0:putchar('\n');}
const int N=5e5+5,M=1005;
int T,n,m;
string a,b;
vector<int> e[N*2];
void add(int a,int b){
e[a].push_back(b);
}
int dfn[N],low[N],dfncnt;
int s[N],tp,scc[N],sc;
bool ins[N];
void tarjan(int u){
dfn[u]=low[u]=++dfncnt;
s[++tp]=u;ins[u]=1;
for(auto v:e[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
++sc;
while(s[tp]!=u){
ins[s[tp]]=0;
scc[s[tp]]=sc;
--tp;
}
ins[s[tp]]=0;
scc[s[tp]]=sc;
--tp;
}
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
#endif
cin>>T;
while(T--){
cin>>n>>m;
//i han i+n man
fill(dfn+1,dfn+2*n+1,0);
fill(low+1,low+2*n+1,0);
fill(ins+1,ins+2*n+1,0);
dfncnt=tp=sc=0;
for(int i=1;i<=n*2;i++) e[i].clear();
for(int i=1;i<=m;i++){
cin>>a>>b;
int A=0,B=0;
for(int j=1;j<a.size();j++) A=A*10+a[j]-'0';
for(int j=1;j<b.size();j++) B=B*10+b[j]-'0';
if(a[0]=='h'){
if(b[0]=='h'){//h h
add(A+n,B);
add(B+n,A);
}else{//h m
add(A+n,B+n);
add(B,A);
}
}else{
if(b[0]=='h'){//m h
add(A,B);
add(B+n,A+n);
}else{//m m
add(A,B+n);
add(B,A+n);
}
}
}
for(int i=1;i<=2*n;i++){
if(!dfn[i]){
tarjan(i);
}
}
bool flag=false;
for(int i=1;i<=n;i++){
if(scc[i]==scc[i+n]) flag=true;
}
cout<<(flag?"BAD\n":"GOOD\n");
}
return 0;
}