2-sat
模型通过巧妙的连边将逻辑关系与图论相对应,通过形象化的图论模型来解决复杂关系问题
对于一个逻辑表达式“若 则 ”,可以通过 状态向 连一条边来表示
那么最终会形成一个有向图,图上一个点所能到达的所有点即是若选取 状态会随之发生的状态
是应用这种思想来解决一个事物有两种状态的问题,实际操作时可以通过增加状态来解决一个事物有多个状态的问题
具体化地,设每个变量都有 和 两种状态
对于形如 取 则 必须取 的问题,可以从 向 连一条边
同时这个条件还意味着如果 取了 则 必须取 ,那么从 向 连边
其他三种关系类似
从这里可以看出, 建出的边是两两对称的
对于建出的图,如果梳理出关系如果 取 则必须取 同时若 取 则必须取 则判为无解,其他均有解
而这里的判定等价于在同一个强联通分量中,有 可以很方便地解决
对于输出方案,如果没有做任何要求,对于每个点选取拓扑序较大的那个
而强连通分量编号小正好对应了拓扑序大
而如果同时限制了字典序或求其他问题,则只好从每个点出发遍历一遍图了
放一道 模板题 的代码
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=4e4+5;
int n,m,x,y,hd[maxn],cnt,dfn[maxn],sta[maxn],tp,low[maxn],c[maxn],rev[maxn],num,tot;
bool vis[maxn];
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
struct Edge{
int nxt,to;
}edge[maxm];
void add(int u,int v){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
hd[u]=cnt;
return ;
}
void tarjan(int u){
dfn[u]=low[u]=++num;
sta[++tp]=u;vis[u]=true;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int v;tot++;
do{
v=sta[tp--];
c[v]=tot;
vis[v]=false;
}while(v!=u);
}
return ;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
rev[i*2-1]=i*2;
rev[i*2]=i*2-1;
}
for(int i=1;i<=m;i++){
x=read(),y=read();
add(x,rev[y]);
add(y,rev[x]);
}
for(int i=1;i<=n*2;i++){
if(!dfn[i])tarjan(i);
}
for(int i=1;i<=n*2;i++){
if(c[i]==c[rev[i]]){
puts("NIE");
return 0;
}
}
for(int i=1;i<=n*2;i+=2){
if(c[i]<c[rev[i]])printf("%d\n",i);
else printf("%d\n",rev[i]);
}
return 0;
}
模型变形
实际题目中通过复杂化背景与状态,或改变要求的量来增加难度
P3007 [USACO11JAN] The Continental Cowngress G
这道题要求出必须与非必须
如果两个状态有指向关系,那么只能选择被指向的,否则都可以
可以用 结合拓扑排序完成
由于 很小,可以直接 完成建图,然而题目要求方案数
可以发现其实在一种合法状态的基础上只能移动最多两个人
如果从 阵营同时移两个人到 阵营那么他们之间不符合题意
只有一种情况是交换两个阵营中的一对人达成目的
那么预处理出每个阵营中对面阵营使得其不能过去的人的个数,只有 和 可以过去
注意根据题意特判其中一个阵营走到没人的情况
首先乍一看状态是 的,可实际上只要当这个人需要成为别人条件的那些时间点有用,于是边点都变成 的
可以发现限制条件的特殊性使得最终这张图是一个
求答案时只需要看图上其活着的状态能到达活着的状态数即可,可以用 来完成,对于空间问题采用分组的方式解决
乍一看每张图都有三种汽车是 种状态的做不了,但是观察发现除了 以外的地图只允许两种汽车,而 的个数很少,显然最后可以单独判断
那么对于限制条件模拟题意建图就好了
对于 的处理可以跑两次,分别将其当做 地图和 地图来对待,这样考虑到了所有车型,而复杂度允许 枚举
题意:定义一个组的 为组内两两矛盾值的最大值,求所有人分成两组的 和最小的情况
首先设
假如已知了 的最大值,那么就二分 ,同时可以列出多个 的式子求解
考虑优化 枚举 最大值的过程
首先求出最大生成树
假如 的最大值不在生成树上
如果两端点距离超过了 ,那么中间树上的边属于了 ,不合法
否则都是距离为 的,特殊判断一下即可(即形成的集合就是黑白染色的集合)
至此,可能最大值在最大生成树上,降为
优化建图
建图是图论的传统难题,自然也可以和 有机结合在一起
对于边的限制很好体现,然而每组只有一个关键点的限制会产生大量连边,而这其中大部分都是冗余的
可以运用前缀优化建图的思想来解决
新增状态 表示这一组前 个都不是关键点
表示这一组前 个中出现了关键点
一个点成为关键点要求前 个没有关键点,且要求前 个有关键点
前 个有关键点要求前 个也有关键点,前 个没有关键点要求前 个也没有关键点
以上条件反之亦然
这样建出的图边点都是 级别的
首先肯定要二分答案
把边的选与不选当做状态,点对周围边的制约当做上一道题中的组,然后一样去做就可以了
一样的道理,将区间 拆分成前缀 和前缀 即可
当然,这一类题比较通用的方法也可以线段树优化建图
Summary
- 能否反应过来是 问题关键在于寻找题目中隐藏的只有两种状态的变量
- 时刻考虑边点的复杂度,综合运用各种方法进行建图优化
- 观察建出图的性质,是否形成了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效