2-SAT
这篇文章是关于SAT做法的记录。
2-SAT是解决关于两个点之间的关系的问题
题目
庆功宴中一共邀请了N对夫妻,每对夫妻中最多只有其中一人出席。在总计2N个人中,某些人之间存在着不共戴天的矛盾(夫妻之间不存在矛盾QWQ),有矛盾的2个人不能同时出席。 现在,给定这2N个人的关系,请回答是否可能有N个人同时出席?
输入
输入包含多组数据,每组中:
1.一个整数\(N\): 邀请了 \(N\) 对夫妻 \((n<= 1000)\)
2.一个整数\(M\): \(2N\) 个人中有 \(M\) 对矛盾关系 \(( M < (N - 1) * (N -1))\)
接下来的 \(M\) 行中,每行 \(4\) 个数字:\(A_1,B_1,A_2,B_2\)
\(A_1,B_1\) 表示夫妻的编号
\(A_2,B_2\)表示妻子还是丈夫 ,\(0\) 表示妻子 ,\(1\) 是丈夫
例:\(2 3 1 0\)表示 \(2\)号 夫妻的丈夫与 \(3\) 号夫妻的妻子有矛盾
夫妻编号从 \(0 \sim n -1\)
输出
如果存在合理的情况,输出 \(YES\)
否则输出 \(NO\)
样例输入
2
1
0 1 1 1
样例输出
YES
思路:
这个样例太水了,我们换一个
4
4
0 1 0 0
0 2 0 1
2 3 1 1
3 1 1 0
先将编号 \(+1\) 方便画图
4
4
1 2 0 0
2 3 0 1
3 4 1 1
2 4 1 0
建完矛盾是下面这样

可以建一个有向图。如果一个点只能到达夫妻中的一个,那就几那一条由其到没有矛盾的人哪里一条边。
建完图后得到下面这样

是不是太乱了?我们把有矛盾的去掉

整理一下,得到

我们从1开始bfs做\(tarjan\)
得到 1,2',4' 3 四个联通块,然后我们弹出这几个点

最后再弹出 3',4,2,1'这就算做完了
但是这个样例太普通了,没有联通块,所以我们再出一个样例
4
4
1 3 0 0
2 1 0 1
2 4 0 0
2 3 1 1
整理完如下:

从1开始遍历会回到1说明这是1个强连通分量
因为我们的定义是只有能到下一个点才会连一条边,所以同一个联通分量不能有夫妻两点,不就会选一个点就必须选他的夫妻,这互相矛盾。
比如下面这样
2
2
1 2 0 0
1 2 1 0

这个就不行
分析完了,上代码
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=2005;
vector<int>g[N];//记录图
int n,m;
int hus(int x)
{
//返回x的丈夫
if(x<=n)x+=n;
else x-=n;
return x;
}
void add(int u,int v)
{
//如果u,v有矛盾,选u就只能选v的丈夫,选v就只能选u的丈夫
g[u].push_back(hus(v));
g[v].push_back(hus(u));
}
int dfn[N];//记录dfs序
int low[N];//记录ta能连的dfn最低
bool bj[N];//记录是否在栈里
int s[N];//记录栈
int tp;//栈顶
int tot;//联通块编号
int cnt=0;//记录dfs搜到第几个点
int b[N];//记录在第几个联通块
void dfs(int x)
{
//tarjan
s[++tp]=x;
dfn[x]=++cnt;
low[x]=cnt;
bj[x]=1;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(!dfn[v]) //如果没被访问,就去dfs它
{
dfs(v);
low[x]=min(low[x],low[v]);//更新low[x]
}
else{
if(!bj[v])//如果被访问了,且v不在栈里,改变low[x]
{
low[x]=min(low[x],low[v]);
}
}
}
if(low[x]==dfn[x])
{
//如果low[x]==dfn[x]说明这是联通块第一个点,弹出联通块
tot++;
while(1)
{
bj[tp]=0;
b[s[tp]]=tot;
tp--;
if(s[tp+1]==x)return ;
}
}
}
void solve()
{
for(int i=1;i<=n*2;i++)
{
//清空
g[i].clear();
bj[i]=0;
b[i]=0;
s[i]=0;
low[i]=0;
dfn[i]=0;
}
tp=0;
tot=0;
cnt=0;
for(int i=1;i<=m;i++)
{
//输入,加边
int x,y,k1,k2;
cin>>x>>y>>k1>>k2;
x++;
y++;
if(k1==1)x=hus(x);
if(k2==1)y=hus(y);
add(x,y);
}
//进行tarjan
for(int i=1;i<=2*n;i++)
{
if(!dfn[i])dfs(i);
}
int flag=0;
for(int i=1;i<=tot;i++)
{
//如果夫妻在一个联通块,输出NO
if(b[i]==b[hus(i)]){
cout<<"NO";
return ;
}
}
cout<<"YES\n";
return ;
}
int main()
{
//关闭cin cout同步提速
std::ios::sync_with_stdio(false);
std::cin.tie(0);
while(cin>>n>>m)
{
solve();
}
}

浙公网安备 33010602011771号