2-SAT

为了方便理解,咱们可以先看一组实例。

今天huge要买水果
lbtl说:
1.我不吃梨(\(\neg a\))
2.我吃苹果(b)
3.我吃草莓(c)
lxyt说:
1.我吃梨(a)
2.我吃苹果(b)
3.我不吃草莓(\(\neg c\)
CTH说:
1.我不吃梨(\(\neg a\)
2.我不吃苹果(\(\neg b\)
3.我不吃草莓(\(\neg c\)
(huge:不吃,滚)

我们不妨根据逻辑运算符给他们设计一下状态
lbtl\((\neg a \vee b \vee c)\)
lxyt\((a \vee b \vee \neg c)\)
CTH\((\neg a \vee \neg b \vee \neg c )\)
而他们同时满足则为
\((\neg a \vee b \vee c) \wedge (a \vee b \vee \neg c) \wedge (\neg a \vee \neg b \vee \neg c )\)

那么很好,我们就需要一个算法来处理这个,2-SAT闪亮登场,那既然它都叫“2”-SAT了,那自然是只有两个限定条件,于是我们把想不想吃草莓这一状态删掉,这样就转化为了了2——SAT类型。

那我们如何去解决2—SAT问题呢?

我们可以想到差分约束,通过建图,在图上进行操作来解决。于是我们就可以用强联通分量来搞它。

那差分约束最困难的地方是连边,2-SAT也是如此。先看一道例题

那我们正常想的连边

点击查看代码
 a=read();x=read();  //第a个数为x或第b个数为y 
        b=read();y=read();
        if(x==0&&y==0)      //"如果第a个数为0或第b个数为0"至少满足其一 
        {
            add(a+n,b);     //a=1则b=0 
            add(b+n,a);     //b=1则a=0 
        }
        if(x==0&&y==1)      //"如果第a个数为0或第b个数为1"至少满足其一 
        {
            add(a+n,b+n);   //a=1则b=1 
            add(b,a);       //b=0则a=0 
        }
        if(x==1&&y==0)      //"如果第a个数为1或第b个数为0"至少满足其一
        {
            add(a,b);       //a=0则b=0 
            add(b+n,a+n);   //b=1则a=1 
        }
        if(x==1&&y==1)      //"如果第a个数为1或第b个数为1"至少满足其一
        {
            add(a,b+n);     //a=0则b=1 
            add(b,a+n);     //b=0则a=1 
        }
关于连边的规律


至于为什么
看看佬的blog

但我们可以利用位运算来简化

点击查看代码
for(int i=1;i<=m;i++)
	{
		int a=read(),va=read(),b=read(),vb=read();
		add(a+n*(va&1),b+n*(vb^1));
		add(b+n*(vb&1),a+n*(va^1));
	}

连完边之后,我们就可以跑tarjan了,但要注意tarjan跑出的是拓扑逆序,最后判断的时候要反过来。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=3e6;
int n,m;
 

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch^48);ch=getchar();}
	return s*f;
}
int h[N],to[N],nxt[N],tot;
void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=h[x];
	h[x]=tot;
}

int low[N],dfn[N],dfncnt;
int s[N],in_stack[N],tp;
int scc,sc[N];
void tarjan(int u)
{
	low[u]=dfn[u]=++dfncnt;
	in_stack[u]=1;
	s[++tp]=u;
	for(int i=h[u];i;i=nxt[i])
	{
		int v=to[i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(in_stack[v])
		{
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(dfn[u]==low[u])
    {
        scc++;
        
        while(s[tp]!=u)
        {
            sc[s[tp]]=scc;
            in_stack[s[tp]]=0;
            tp--;
        }
        sc[s[tp]]=scc;
        in_stack[s[tp]]=0;
        tp--;
    }
}



int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		int a=read(),va=read(),b=read(),vb=read();
		add(a+n*(va&1),b+n*(vb^1));
		add(b+n*(vb&1),a+n*(va^1));
	}
	for(int i=1;i<=n*2;i++) if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;i++) 
	{
		if(sc[i]==sc[i+n]) 
		{
			puts("IMPOSSIBLE");
			exit(0);
		}
	}
	puts("POSSIBLE");
	for(int i=1;i<=n;i++)
	{
		printf("%d ",sc[i]<sc[i+n]);
	}
}
posted @ 2024-07-30 17:50  zhengchenxi  阅读(48)  评论(1)    收藏  举报