2-sat

萌新刚学 2-sat,写个题解练练手。


2-sat


首先的话,我个人理解的 2-sat 就是对于每个数只有 2 种取值的问题。

显然位运算的题,就是一个经典的 2-sat 的题,因为对于每个数都只有 01 两种选择。


卡图难题


具体思路

这里我以 卡图难题 这题为例。

那么我们将每个点 ai 拆成 aiai+n 两个点,分别表示 ai 的值为 0ai 的值为 1

然后就是对于每种关系建边。

  • a and b=0
    1. a=0,那么 b 的取值是 01 都无所谓,因此 a=0 就不需要建边来保证关系。b=0 同理。
    2. a=1,那么 b 的取值只能是 0,因此要连边 ins(a+n,b)b=1 同理。
    3. ins(a+n,b),ins(b+n,a)
  • a and b=1
    1. a=0,那么 b 的取值是 01 都无法成立,因此 a=0 的情况是无解的,连边 ins(a,a+n)b=0 同理。
    2. a=1,那么 b 的取值只能是 1,然而第一种情况保证了 b0,因此不需要连边。b=1 同理。
    3. ins(a,a+n),ins(b,b+n)
  • a or b=0
    1. a=1,那么 b 的取值是 01 都无法成立,因此 a=1 的情况是无解的,连边 ins(a+n,a)b=1 同理。
    2. a=0,那么 b 的取值只能是 0,然而第一种情况保证了 b1,因此不需要连边。b=0 同理。
    3. ins(a+n,a),ins(b+n,b)
  • a or b=1
    1. a=0,那么 b 的取值只能是 1,因此要连边 ins(a,b+n)b=0 同理。
    2. a=1,那么 b 的取值是 01 都无所谓,因此 a=1 就不需要建边来保证关系。b=1 同理。
    3. ins(a,b+n),ins(b,a+n)
  • a xor b=0
    1. a=0,那么 b 的取值只能是 0,因此要连边 ins(a,b)b=0 同理。
    2. a=1,那么 b 的取值只能是 1,因此要连边 ins(a+n,b+n)b=1 同理。
    3. ins(a,b),ins(b,a),ins(a+n,b+n),ins(b+n,a+n)
  • a xor b=1
    1. a=0,那么 b 的取值只能是 1,因此要连边 ins(a,b+n)b=0 同理。
    2. a=1,那么 b 的取值只能是 0,因此要连边 ins(a+n,b)b=1 同理。
    3. ins(a,b+n),ins(b,a+n),ins(a+n,b),ins(b+n,a)

然后的话看有没有矛盾,即在图上跑一边 Tarjan,找出强连通分量,然后看有没有 aiai+n 同时在同一个强连通分量里面。

因为如果 aiai+n 同时在一个强连通分量中的话,那么 aiai+n 之间就可以互相到达,即 ai 同时为 01,因此矛盾。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=2100,M=4110000;
struct edge{int x,y,pre;}a[M];
int last[N],alen;
void ins(int x,int y){
	a[++alen]=edge{x,y,last[x]};
	last[x]=alen;
}
int dfn[N],low[N],v[N],id;
int top,sta[N];
int cnt,c[N];
void tarjan(int x){
	dfn[x]=low[x]=++id;v[x]=1;
	sta[++top]=x;
	for(int k=last[x];k;k=a[k].pre){
		int y=a[k].y;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(v[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		cnt++;
		int y;
		do{
			y=sta[top--];
			v[y]=0;
			c[y]=cnt;
		}while(y!=x);
	}
}
int main(){
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int a,b,c;char op[10];
		scanf("%d%d%d%s",&a,&b,&c,op+1);
		if(op[1]=='A'){
			if(c==0)
				ins(a+n,b),ins(b+n,a);
			if(c==1)
				ins(a,a+n),ins(b,b+n);
		}
		if(op[1]=='O'){
			if(c==0)
				ins(a+n,a),ins(b+n,b);
			if(c==1)
				ins(a,b+n),ins(b,a+n);
		}
		if(op[1]=='X'){
			if(c==0)
				ins(a,b),ins(b,a),ins(a+n,b+n),ins(b+n,a+n);
			if(c==1)
				ins(a,b+n),ins(b,a+n),ins(a+n,b),ins(b+n,a);
		}
	}
	for(int i=1;i<=2*n;i++){
		if(!dfn[i])tarjan(i);
	}
	int bk=0;
	for(int i=1;i<=n;i++){
		if(c[i]==c[i+n]){
			bk=1;
			break;
		}
	}
	if(!bk)puts("YES");
	else puts("NO");
	return 0;
}
posted @   reclusive2007  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示