2-sat 学习笔记
学习笔记
有这样一类问题,有多个变量
思想
考虑将每一个点拆成两个点,分别表示是否选择该取值,那么显然两个中只能有一个满足。以这一条为基础,我们想要证明整个方程无解必须说明当一个取值为
考虑如何构造,将强连通分量所点后得到一个 Tarjan
实现时强连通分量的编号相当于逆拓扑序,也就是反着的拓扑序,我们取所在强连通分量编号较小的那个点就行。时间复杂度为
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define id(x,t) ((t)?x:x+n)
const int N=1e6+5;
int n,m,cnt,num,top,scc,tot;
int head[N<<1],dfn[N<<1],low[N<<1],col[N<<1],stk[N<<1];
struct Edge{
int v,to;
}e[N<<1];
il void AddEdge(int x,int y){
e[cnt]={y,head[x]};
head[x]=cnt++;
return ;
}
il void Tarjan(int x){
dfn[x]=low[x]=++num;
stk[++top]=x;
for(int i=head[x];i;i=e[i].to){
int y=e[i].v;
if(!dfn[y]){
Tarjan(y);
low[x]=min(low[x],low[y]);
}else if(!col[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
++scc;
int y;
do{
y=stk[top--];
col[y]=scc;
}while(y!=x);
}
return ;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,a,y,b;cin>>x>>a>>y>>b;
AddEdge(id(x,a^1),id(y,b));
AddEdge(id(y,b^1),id(x,a));
}
for(int i=1;i<=n*2;i++){
if(!dfn[i])Tarjan(i);
}
for(int i=1;i<=n;i++){
if(col[i]==col[i+n]){
cout<<"IMPOSSIBLE";
return 0;
}
}
cout<<"POSSIBLE\n";
for(int i=1;i<=n;i++)cout<<(col[i]<col[i+n])<<" ";
return 0;
}
常见模型
赋值型
形如
推导型
形如
逻辑或型
形如
逻辑与型
形如
逻辑异或型
形如
常见优化
无用点忽略
指一些点只起到传递作用,而没有其他限制作用,这样的点在一定程度上是无用的,为了节省复杂度可以直接删去。
传递点加入
在一些边数过多的图中,我们可以用一些传递点(也就是单独的一个点,没有配对)来传递信息,这样我们可以用一条边代替多条边的作用,起到优化的效果。比如对于
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律