2-sat问题

什么是2-sat问题?


2-sat问题就是2判定问题,是一种特殊的逻辑判断问题。下面看一道例题来认识一下2-sat问题:

HDU3062:http://acm.hdu.edu.cn/showproblem.php?pid=3062

Problem Description

有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?

Input

n: 表示有n对夫妻被邀请 (n<= 1000)
m: 表示有m 对矛盾关系 ( m < (n - 1) * (n -1))

在接下来的m行中,每行会有4个数字,分别是 A1,A2,C1,C2 
A1,A2分别表示是夫妻的编号 
C1,C2 表示是妻子还是丈夫 ,0表示妻子 ,1是丈夫
夫妻编号从 0 到 n -1 

Output
如果存在一种情况 则输出YES 
否则输出 NO 

Sample Input

1
0 1 1 1 

Sample Output

YES

原题可描述为:

n个组,第i个组里有两个节点Ai,Ai'。需要从每个组中选出一个。而某些点不可以同时选出(称之为不相容)。任务是保证选出的n个点都能两两相容。

如果Ai和Aj不相容,那么选择Ai就必须选择Aj‘;同样,选择Aj就必须选择Ai'。Ai->Aj' 和Aj->Ai'相互对称。

算法一:

举例说明:

假设有4个组,不和的代表有:142373,那么构图:


假设:首先选1

3必须选,2不可选

8必须选,47不可选

5、6随便选一个

矛盾的情况为:存在Ai,使得Ai必须被选又不可选。

得到 算法 1
枚举每一对尚未确定的 A i ,A i ,任选 1 个,推导出相关的组,若不矛盾,则可选择;否则选另 1 个,同样推导。若矛盾,问题必定无解。
算法的时间复杂度在最坏的情况下为 O(nm)
在这个算法中,并没有很好的利用图中边的 对称

算法二:
图的收缩:
现在仔细看看上图,1、3构成了一个环,即1、3要么都被选或者都不被选。同理,2、4也是如此。
更一般的说:
在每个一个环里,任意一个点的选择代表将要选择此环里的每一个点。不妨把环收缩成一个子节点(规定这样的环是 极大强连通子图 )。新节点的选择表示选择这个节点所对应的环中的每一个节点。

对于原图中的每条边 A i-> A j (设 A i 属于环 S i A j 属于环 S j )如果 S i ≠S j 则在新图中连边: S i-> S j

这样就构成了新的有向无环图,此图和原图等价。

通过 求强连通分量 ,可以把图转换成新的有向无环图,在这个基础上,介绍一个新的算法。
新算法中,如果存在一对 A i ,A i ' 属于同一个环,则判无解,否则将采用 拓扑排序 ,以自底向上的顺序进行推导,一定能找到可行解。
至于这个算法的得来及正确性,将在下一段文字中进行详细分析。

深入分析:

回忆构图的过程:
对于两个不相容的点 A i , A j ,构图方式为:

    Ai->Aj

    Aj->Ai'

前面提到过,这样的两条边 对称 ,也就是说:
如果存在 A i-> A j ,必定存在 A j '->A i '

引理:原图具有对称传递性

等价于:Ai->Ak, Ak'->Ai' (->代表一种传递关系)

猜测一:图中的环分别对称

如果存在 A i ,A j A i ,A j 属于同一个环(记作 S i ),那么 A i ', A j ' 也必定属于一个环(记作 S i ' ),由引理不难得出每个环分别对称。
推广一:新图中同样具有对称传递性。
更加一般的情况,如下图( 说明:此图中 S i ' 有可能为 S i 的后代节点):

于是可以得到
推广二:对于任意一对Si, Si'Si的后代节点与Si'的前代节点相互对称
继而提出
猜测二:若问题无解,则必然存在Ai, Ai',使得Ai, Ai属于同一个环。
也就是,如果每一对 A i ,A i ' 都不属于同一个环,问题必定有解。
下面给出简略证明:
先提出一个跟算法一相似的步骤:
如果选择 S i ,那么对于所有 S i-> S j S j 都必须被选择。
S i ' 必定不可选,这样 S i 的所有前代节点也必定不可选(将这一过程称之为 删除 )。
推广二 可以得到,这样的删除不会导致矛盾。

假设选择S3'

->选择S3'的后代节点,S1'

->删除S3

->删除S3的前代节点S1

->S1S1'对称

每次找到一个未被确定的 S i ,使得不存在 S i-> S i ',选择Si及其后代节点而删除Si’及Si‘的前代节点。一定可以构造出一组可行解。
因此 猜测 2 成立。

另外,若每次盲目的去找一个未被确定的 S i ,时间复杂度相当高。
自底向上 的顺序进行选择、删除,这样还可以免去“ 选择 S i 的后代节点 这一步。
拓扑排序 实现自底向上的顺序。

算法二的流程:
1 .构图
2 .求图的极大强连通子图
3 .把每个子图收缩成单个节点,根据原图关系构造一个有向无环图
4 .判断是否有解,无解则输出(退出)
5 .对新图进行拓扑排序
6 .自底向上进行选择、删除
7 .输出

HDU3062代码:
#include<cstdio>
#include<cstring>
usingnamespace std;
#define MIN(x,y) (x<y?x:y)
#define N 2005

struct Edge
{
	int vtx,next;
}E[999999];
int head[N],size;
int dfn[N],low[N],blg[N],step,scc;
int stk[N],top;
bool ins[N];

void Init()
{
    memset(head,-1,sizeof(head));
    memset(dfn,-1,sizeof(dfn));
    memset(ins,false,sizeof(ins));
    size=step=scc=0; top=-1;
}

void Insert(int u,int v)
{
    E[size].vtx=v;
    E[size].next=head[u];
    head[u]=size++;
}

void Tarjan(int u)
{
    stk[++top]=u; ins[u]=true;
    dfn[u]=low[u]=step++;
	for(int i=head[u];i!=-1;i=E[i].next)
	{
		int v=E[i].vtx;
		if(dfn[v]==-1)
		{
			Tarjan(v);
			low[u]=MIN(low[u],low[v]);
		}
		else if(ins[v])
		{
			low[u]=MIN(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u])
	{
		for(int v=-1;v!=u;top--)
		{
			v=stk[top];
			ins[v]=false;
			blg[v]=scc;
		}
		scc++;
    }
}

int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		Init();
		for(int i=0;i<m;i++)
		{
			int a1,a2,c1,c2;
			scanf("%d%d%d%d",&a1,&a2,&c1,&c2);
			Insert((a1<<1)+c1,(a2<<1|1)-c2);
			Insert((a2<<1)+c2,(a1<<1|1)-c1);
		}
		for(int i=0;i<2*n;i++)
		{
			if(dfn[i]==-1) Tarjan(i);
		}
		bool flag=true;
		for(int i=0;i<n;i++)
		{
			if(blg[i<<1]==blg[i<<1|1])
			{
				flag=false;
				break;
			}
		}
		puts(flag?"YES":"NO");
	}
}
更多请参考: 2-sat专题
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值