2-SAT(记录)

· · 个人记录

2-SAT

① 是指一类题型

——在n对数中在每两对的两个数中选一个是否可以满足选够n个数

(1)例如以下问题

——>[party](https://acm.hdu.edu.cn/showproblem.php?pid=3062)\ $也可以从这里$\ ——>[A-party](https://vjudge.net/contest/704558#problem/A)

解析如下

(一)建图分析

声明 A的伴侣为A',则B的伴侣为B'

$我们知道如果A和B有矛盾的话 那么为了尽量满足前去n人去参加

我们就有了两种选择

①让 A和B' 去

②让 B和A'去

这样我们就不难想到

假设存在这样一组矛盾关系

我们就可以建两条有向边

第一条边是由A指向B'的

第二条边是由B'指向A的

(1)每条有向边(u,v)表示从u指向v

①u选v必选

②但v选u无限制

(2)

那么由此可得

①这每个点表示选这个点时

②其所连的的有向边所能遍历到的所有点都得选

(二)判断解是否存在

然后我们去判断是否无解,如果有就进行有的操作

我们先判断是否无解

在这个题里无解即为两种

①矛盾同出现

②夫妻同出现

由于我们先前的存储方式,①即为解决了

再看(夫妻同出现的问题)

在这个题里,在我们的建图方法下

如果在一对夫妻里我们选谁,其伴侣都必须选的话

那即为无解,因为题目里要求了,夫妻不同现嘛

(三)转化题目

我们现在在看这个无解的情况

"互选"不就是强连通分量吗

似乎与可互相到达有异曲同工之妙

夫妻不同现,也即为夫妻这两所代表的点不在同一个强连通分量里\

这个题也就转化成为了求图中强连通分量

这个可以用tarjan+栈解决

(四)奉上Code


#include<bits/stdc++.h>
#define N 2500
#define M 1000000
using namespace std;
int n,m,cnt=0,b=0,top=0,ans=0;
int s[N],ins[N],dfn[N],low[N],scc[N];
vector<int>v[N<<1];
int read();
int wf(int u){return u<<1;}
int hs(int u){return u<<1|1;}
void tarjan(int u);
void add(int from,int to){v[from].push_back(to);}

int main() { ios::sync_with_stdio(false); cin.tie(0); while(cin>>n>>m){
for(int i=1;i<=m;i++){ //int a1=read(),a2=read(),c1=read(),c2=read(); int a1,a2,c1,c2; cin>>a1>>a2>>c1>>c2; int f=(c1?hs(a1):wf(a1)),s=(c2?hs(a2):wf(a2)); add(f,(s^1)); add(s,(f^1)); } for(int i=0;i<(n<<1);i++){ if(dfn[i])continue; tarjan(i); } for(int i=0;i<(n<<1);i+=2){ if(scc[i]==scc[i^1]){ b=1; break; } } if(!b)cout<<"YES\n"; else cout<<"NO\n"; cnt=0,ans=0,b=0; for(int i=0;i<(n<<1);i++)dfn[i]=0; for(int i=0;i<(n<<1);i++)ins[i]=0; for(int i=0;i<(n<<1);i++)v[i].clear(); } return 0; } int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } void tarjan(int u){ low[u]=dfn[u]=++cnt; ins[u]=1; s[++top]=u; for(int i=0;i<v[u].size();i++){ int g=v[u][i]; if(!dfn[g]){//Q1,u->g tarjan(g); low[u]=min(low[u],low[g]); } else if(ins[g]){//Q2,u->g low[u]=min(low[u],dfn[g]);//Q3,u->g } } if(dfn[u]==low[u]){ ++ans; while(1){ scc[s[top]]=ans; ins[s[top]]=0; if(s[top--]==u)break;//Q4,->top位置不对 } } return ; }