2-SAT 问题
SAT 问题
-
SAT: Satisfiability 满足
-
给出很多个包含多个命题的条件,给出命题的真假方案,使得所有条件成立
-
如:对于命题 使得 成立
2-SAT问题
-
每个条件包含两个命题的SAT问题
-
如:对于 使得 成立
-
转化:建图
-
用 表示 ,用 表示
-
用图论里的每一个点表示每一个命题,每一条边表示每两个命题间的推导关系
-
推导关系:
-
即:
-
那么我们可以把每一个条件表示为图论中的边了
-
-
解的情况
-
无解:
- 即 和 在同一个强连通分量里,说明 不管取真或假都是相反的,这就有问题了
-
有解:枚举所有 ,缩点找强连通分量,当 所在的强连通分量的拓扑排序在 所在的强连通分量的拓扑排序之后时,取 为
例题P4782 【模板】2-SAT 问题
#include<bits/stdc++.h>
#define rint register int
#define int long long
#define endl '\n'
using namespace std;
const int N = 2e6 + 5;
const int M = N;
int h[N],e[M],ne[M],idx,dfn[N],low[N],sk[N],ins[N],id[N],top,ts,n,m,cnt;
int inline min(int a,int b){
return a<b?a:b;
}
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void inline tarjan(int u){
sk[++top]=u;ins[u]=1;
dfn[u]=low[u]=++ts;
for(rint i=h[u];~i;i=ne[i]){
if(!dfn[e[i]]) {
tarjan(e[i]);
low[u]=min(low[e[i]],low[u]);
}
else if(ins[e[i]]){
low[u]=min(dfn[e[i]],low[u]);
}
}
if(dfn[u]==low[u]){
int j;
cnt++;
do{
j=sk[top--];
id[j]=cnt;
ins[j]=0;
}
while(j!=u);
}
return ;
}
signed main(){
scanf("%lld%lld",&n,&m);
memset(h,-1,sizeof h);
while(m--){
int i,a,j,b;
scanf("%lld%lld%lld%lld",&a,&i,&b,&j);
a--;
b--;
add(2*a+!i,2*b+j);
add(2*b+!j,2*a+i);
}
for(rint i=0;i<2*n;i++){
if(!dfn[i]){
tarjan(i);
}
}
for(rint i=0;i<n;i++){
if(id[i*2]==id[i*2+1]){
puts("IMPOSSIBLE");
return 0;
}
}
puts("POSSIBLE");
for(rint i=0;i<n;i++){
if(id[i*2]<id[i*2+1]){
printf("0 ");
}
else{
printf("1 ");
}
}
return 0;
}
本文作者:PassName
本文链接:https://www.cnblogs.com/spaceswalker/p/16510471.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步