`
yzmduncan
  • 浏览: 330402 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

2-SAT

阅读更多

    2-SAT是求解一组逻辑变量表达式(avb)^(cvd)...(xvy)=1成立的问题,称为适定性问题(Satisfiability),简称SAT。

    对于逻辑表达式(xvy),构造有向图G,也就是说,x和y至少选一个:如果不选x,肯定要选y;不选y,肯定要选x。在图G中添加两条有向边(~x,y)和(~y,x)。

    对于逻辑表达式(x^y),x和y都要选:若选x,则必选y(x,y);选y,必选x(y,x);不选x,则无解(~x,x);不选y,则无解(~y,y)。

    然后求图G的强连通分量,对于图中的任何一个强连通分量,如果我们选择其中的任何一个点,那么该SCC中所有其它顶点也必须被选择。如果x和~x属于同一个连通分量,那么产生矛盾,该2-SAT无解。如果没有产生矛盾,将图缩点,在其反向图中用拓扑排序和染色操作可以求出一组解。

    关于2-SAT的详细介绍,可以参考赵爽和伍昱的论文。

      例:HDU3062

      题意:有n对夫妻收到一个派对的邀请,由于场地限制,只能让丈夫和妻子其中一位参加(必须参加);并且,有些人之间有仇恨,不能同时出现在派对上。问最终能否有一个方案,使得有n个人能参加这个派对。

      解:0~n-1表示妻子参加派对(x),n~2*n-1表示丈夫参加派对(~x)。对于仇恨的一对(x,y),即x和y中至少选一个。

 

      例:POJ2723

      题意:有n重门,每把门上有2把锁,打开其中一把这个门就算打开了。现在有2m把钥匙,分为m串,每串2把钥匙,使用其中一把,另一把也自动消失。问最多能打开几重门。

      解:能打开n重门,肯定可以打开n-1重门。首先二分答案,对于前k重门的锁(x,y),x和y至少要打开一把,即(~x,y)和(~y,x)。对于这m串钥匙(x,y),x和y不能同时取,即(x,~y)和(y,~x)。

 

      例:POJ3207

      题意:有一个圆,圆上有n个点0,1,2,..,n-1。现在想在某两点之间连一条线,这条线可以画在圆内或者圆外。现在有m对点需要连线,问是否有一种方案,使得这些连线之间没有交点。

      解:一条线只有两种状态,圆内x或者圆外~x。对任意的两条连线:若他们构成矩形的对角线(即有可能相交),则如果一条在圆内,另一条只能在圆外。即如果两条连线x和y的端点分别为(a,b)和(c,d),如果a<c&&c<b<d或者c<a&&a<d<b(即线段相交),那么有边(x,~y),(~x,y),(y,~x),(~y,x)。最后看是否有x和~x属于同一个scc。

 

      例:POJ3683

      题意:9月1号是牧师john最忙的一天,他要帮这天结婚的人举行一个仪式。每对新人的婚礼时间从si到ti,仪式的持续时间是di,并且仪式只能在婚礼开始或者结束的时候做,即仪式在si到si+di或者ti-di到di举行。当然,john在同一时间只能主持一对新人的仪式。问是否有一种方案,使得每对新人都可以顺利的举行仪式,有的话输出任意一种方案。

      解:每对新人举行仪式的时间要么在开始(x),要么在结束(~x)。对于每两对新人a和b,分四种情况:a开始b开始,a开始b结束,a结束b开始,a结束b结束。如果其中有时间段重复(重复的条件是线段有公共部分,线段(a,b)和(c,d)有公共部分的条件是a<d&&c<b),例如a开始b开始有重复,则有边(a,~b),(b,~a)。然后求强连通并缩点,将缩点后的图反向,用拓扑排序,求出一组可行的解。

/*
2-sat
构图:
每对新人的仪式可以在开始和结束做。

把每对新人可以做ceremony的两个时间段记为两个顶点, 枚举所有点的冲突情况构图, 这里判断冲突可以这样做: 
设顶点A的表示时间段[a1,b1], 顶点B表示时间段[a2,b2], 发生冲突的充要条件是a1<b2&&a2<b1(两条线段有公共部分).

然后就是2-SAT的标准算法, 做强连通, 缩点, 反向拓扑排序, 红蓝标记等等,
赵爽的论文上说得很清楚, 只是这里不需要像论文上说的每次在标记红色后都把不相容的顶点及及其子节点也标记蓝色, 
只需要标记不相容顶点即可, 不用DFS取标记子节点.
*/
#include <iostream>
#include <queue>
using namespace std;
const int MAX = 2005;
int n;
char s[MAX/2][6],t[MAX/2][6];
int d[MAX/2];

struct Edge
{
	int from;	//用于缩图方便
	int to;
	int next;
}e[4000000],e2[4000000];
int index[MAX],edgeNum;
int index2[MAX],edgeNum2;

int dfn[MAX],low[MAX],seq;
int stack[MAX],top;
bool inStack[MAX];
int belong[MAX],cnt;

int color[MAX];		//0未着色,1红色,-1蓝色
int indegree[MAX];	//入度,用于拓扑排序
int opp[MAX];		//连通分量i的矛盾方为cf[i]
bool ans[MAX];

int min(int x, int y)
{
	return x<y?x:y;
}

void addEdge(int from, int to)
{
	e[edgeNum].from = from;
	e[edgeNum].to = to;
	e[edgeNum].next = index[from];
	index[from] = edgeNum++;
}

void addEdge2(int from, int to)
{
	e2[edgeNum2].from = from;
	e2[edgeNum2].to = to;
	e2[edgeNum2].next = index2[from];
	index2[from] = edgeNum2++;
}

void addTime(char *t, int len, char* tt)
{
	int hour1,minute1;	//原来的时间
	int hour,minute;	//时间增量
	hour1 = (t[0]-'0')*10 + t[1]-'0';
	minute1 = (t[3]-'0')*10 + t[4]-'0';
	hour = len/60;
	minute = len%60;
	hour += hour1;
	minute += minute1;
	while(minute < 0)
	{
		hour--;
		minute += 60;
	}
	while(minute >= 60)
	{
		hour++;
		minute -= 60;
	}
	tt[0]=hour/10+'0'; tt[1]=hour%10+'0';
	tt[2]=':';
	tt[3]=minute/10+'0'; tt[4]=minute%10+'0';
	tt[5] = '\0';
}

bool check(char* t1, int len1,char *t2, int len2)	//判断两个时间段是否有重合
{
	char *a1,*b1,*a2,*b2;
	char t11[6],t22[6];
	addTime(t1,len1,t11);
	addTime(t2,len2,t22);
	if(len1 > 0)
	{
		a1 = t1;
		b1 = t11;
	}
	else
	{
		a1 = t11;
		b1 = t1;
	}
	if(len2 > 0)
	{
		a2 = t2;
		b2 = t22;
	}
	else
	{
		a2 = t22;
		b2 = t2;
	}
	if( (strcmp(a1,b2)<0&&strcmp(a2,b1)<0) || 
		(strcmp(a2,b1)<0&&strcmp(a1,b2)<0) )	//两个区间有公共部分的条件
		return true;
	return false;
}

void tarjan(int u)
{
	dfn[u] = low[u] = seq++;
	stack[top++] = u;
	inStack[u] = true;
	for(int i = index[u]; i != -1; i = e[i].next)
	{
		int w = e[i].to;
		if(dfn[w]<0)
		{
			tarjan(w);
			low[u] = min(low[u],low[w]);
		}
		else if(inStack[w])
			low[u] = min(low[u],dfn[w]);
	}
	if(dfn[u] == low[u])
	{
		int v;
		cnt++;
		do
		{
			top--;
			v = stack[top];
			inStack[v] = false;
			belong[v] = cnt;
		}while(u!=v);
	}
}

bool solve()
{
	int i;
	int t = n<<1;
	for(i = 0; i < t; i++)
	{
		if(dfn[i] < 0)
			tarjan(i);
	}
	for(i = 0; i < n; i++)
	{
		if(belong[i] == belong[i+n])
			return false;
		opp[belong[i]] = belong[i+n];
		opp[belong[i+n]] = belong[i];
	}
	return true;
}

void solve1()
{
	int i;
	queue<int> que;
	memset(color,0,sizeof(color));
	for(i = 0; i < edgeNum; i++)
	{
		if(belong[e[i].from] != belong[e[i].to])
		{
			addEdge2(belong[e[i].to], belong[e[i].from]);	//反向图
			indegree[belong[e[i].from]]++;
		}
	}
	for(i = 1; i <= cnt; i++)
		if(indegree[i] == 0)
			que.push(i);
	while(!que.empty())
	{
		int now = que.front();
		if(color[now]==0)
		{
			color[now] = 1;			//置为红色
			color[opp[now]] = -1;	//将它的矛盾置为绿色
		}
		que.pop();
		for(i = index2[now]; i != -1; i = e2[i].next)
		{
			int next = e2[i].to;
			indegree[next]--;
			if(indegree[next]==0)
				que.push(next);
		}
	}
	memset(ans,0,sizeof(ans));
	for(i = 0; i < 2*n; i++)	//2-sat的一组解就等价于所有缩点后点颜色为红色的点
	{
		if(color[belong[i]]==1)
			ans[i] = true;
	}
}

int main()
{
	int i,j;
	edgeNum = edgeNum2 = seq = top = cnt = 0;
	memset(dfn,-1,sizeof(dfn));
	memset(index,-1,sizeof(index));
	memset(index2,-1,sizeof(index2));
	memset(inStack,0,sizeof(inStack));
	memset(indegree,0,sizeof(indegree));
	memset(opp,0,sizeof(opp));
	scanf("%d",&n);
	for(i = 0; i < n; i++)
		scanf("%s %s %d",s[i],t[i],&d[i]);
	for(i = 0; i < n; i++)
	{
		for(j = i+1; j < n; j++)
		{
			//i前j前
			if(check(s[i],d[i],s[j],d[j]))
			{
				addEdge(i,j+n);
				addEdge(j,i+n);
			}
			//i前j后
			if(check(s[i],d[i],t[j],-d[j]))
			{
				addEdge(i,j);
				addEdge(j+n,i+n);
			}
			//i后j前
			if(check(t[i],-d[i],s[j],d[j]))
			{
				addEdge(i+n,j+n);
				addEdge(j,i);
			}
			//i后j后
			if(check(t[i],-d[i],t[j],-d[j]))
			{
				addEdge(i+n,j);
				addEdge(j+n,i);
			}
		}
	}
	if(solve())
	{
		printf("YES\n");
		//缩点得到的图反向
		//用拓扑排序得到一个解
		solve1();
		char result[6];
		for(i = 0; i < n; i++)
		{
			if(ans[i])	//开始的时候举行
			{
				addTime(s[i],d[i],result);
				printf("%s %s\n",s[i],result);
			}
			else
			{
				addTime(t[i],-d[i],result);
				printf("%s %s\n",result,t[i]);
			}
		}
	}
	else
		printf("NO\n");
	return 0;
}

 

 

 

分享到:
评论

相关推荐

    2-SAT学习资料

    2-SAT,全称为2-CNF-SAT,是约束满足问题(Constraint Satisfaction Problem, CSP)的一个子类,属于计算机科学中的理论基础。2-SAT问题涉及到逻辑推理和图论,通常表现为解决布尔变量的二元关系。在这个问题中,...

    2-SAT 经典讲解 ACM必备

    《2-SAT经典讲解:ACM竞赛必备》 2-SAT,全称为2-CNF(Conjunctive Normal Form)Satisfiability问题,是逻辑推理领域的一个基础问题,尤其在计算机科学竞赛如ACM(国际大学生程序设计竞赛)中常被考察。2-SAT问题...

    由对称性解2-SAT问题

    《由对称性解2-SAT问题》 2-SAT问题是一种特殊的逻辑判定问题,它涉及到布尔逻辑中的二元约束。在这个问题中,我们处理的是形式化的布尔公式,其中每个子句都包含两个互补的变量或其否定。目标是判断是否存在一个...

    基于寻找2-SAT子问题的SAT算法

    ### 基于寻找2-SAT子问题的SAT算法 #### 概述 在理论计算机科学领域中,可满足性问题(Satisfiability Problem, SAT)一直是研究的热点之一。SAT问题是指对于一个由布尔变量构成的合取范式(Conjunctive Normal ...

    ACM-2-sat资料

    《ACM-2-SAT问题详解》 在计算机科学领域,特别是算法竞赛(如ACM国际大学生程序设计竞赛)中,2-SAT问题是一个重要的理论基础。2-SAT,全称为2-CNF-SAT(2-变量析取范式满足问题),是SAT问题的一个特殊子集,具有...

    top_2-sat演示_2-SAT_algorithm_

    2-SAT(二元满足问题)是图论和计算复杂性理论中的一个重要概念,它属于布尔满足问题(SAT)的一种特殊形式。在这个问题中,我们处理的是由一系列双变量的布尔表达式组成的公式,每个表达式都是一对互补变量,如x和...

    ACM之2-sat

    在计算机科学领域,特别是算法竞赛(ACM,即国际大学生程序设计竞赛)中,2-SAT问题是一个重要的图论和逻辑满足问题。2-SAT,全称为2-CNF-SAT,是约束满足问题(CNF-SAT)的一个特例,其中每个子句都由两个变量或...

    2-sat 求解

    ### 2-SAT (2-Satisfiability) 解决方案及应用 #### 1. 什么是 2-SAT? 2-SAT(2-Satisfiability),即2-可满足性问题,是布尔可满足性问题的一个特殊情况。它涉及到一个由变量及其否定形式组成的公式,其中每个...

    2-sat.rar_2sat

    《2-SAT问题详解与解决方案》 在计算机科学领域,特别是在图论和逻辑满足问题中,2-SAT(2-CNF-SAT)是一个基础且重要的问题。2-SAT问题源于布尔逻辑中的二元约束满足问题(CNF-SAT),但其简化了原问题的复杂性,...

    2-sat.rar_2sat_SAT求解

    《2-SAT问题详解与求解算法探讨》 在计算机科学领域,特别是在理论计算机科学和人工智能中,2-SAT(2-Satisfiability)问题是一个重要的子集。2-SAT是布尔满足问题(SAT问题)的一个简化版本,它涉及到确定一个布尔...

    2-sat.zip_2sat算法

    《2-SAT问题及其高效算法实现》 2-SAT(二元满足问题)是图论中的一个经典子问题,属于布尔可满足性问题(SAT)的一种简化形式。在布尔逻辑中,2-SAT指的是所有布尔变量的二元约束条件集合,其中每个条件都是形如x ...

    2-sat.rar_2sat_worst case

    标题"2-sat.rar_2sat_worst_case"指出,这个压缩包中的资源与解决2-SAT问题有关,并且特别提到了最坏情况下的时间复杂度。这通常意味着包含的代码或算法是针对解决2-SAT问题的,并且可能会讨论在最坏情况下的性能...

    图论- 2-SAT 问题.rar

    这使得2-SAT成为一个在理论和实践中都相对容易处理的问题,尤其是在相比于更一般的SAT问题时,3-SAT和更高阶的SAT问题通常具有NP完全的复杂性。 总的来说,2-SAT问题的解决方法和应用展示了图论在计算机科学中的...

    2-sat好文1

    在2-SAT问题中,这种性质使得我们可以找到一组解,或者证明不存在解。 2-SAT问题的核心在于它是一种特殊的SAT问题,其中每个子句都包含两个不同的变量,每个变量或者取反,或者不取反。例如,子句 (x OR y') 和 (x'...

    Tarjan及2-SAT讲课PPT

    《Tarjan算法与2-SAT详解》 Tarjan算法,源于著名计算机科学家Robert Tarjan,是一种在图论中广泛使用的高效算法,尤其在处理强连通分量的问题上展现出强大的能力。本文将深入探讨Tarjan算法的核心思想及其在2-SAT...

    2-sat---hdu3062

    2-sat---hdu3062,代码详尽,清晰,格式规范,亲测无误。

Global site tag (gtag.js) - Google Analytics