poj 3683 2-SAT (牧师能参加所有的婚礼仪式吗)

题意:有若干对新人结婚,每个婚礼都会一个特殊仪式。对于每个婚礼有三项输入:开始时间(s)、结束时间(t)和特殊仪式的持续时间(last)。已知特殊仪式只能在婚礼的开始阶段或者结束阶段举行(即在s~s+last或者t-last~t)。特殊仪式必须要有牧师才行,而该村只有一个牧师,问合理安排各个婚礼的特殊仪式时间能否使得牧师参加所有的婚礼,如果可以,输出特殊仪式举行时间的一种方案?

思路:显然为2-SAT,难点在输出方案。

需要输出方案的2-SAT总结如下:

1、构图。

2、tarjan求极大强连通子图。

3、判断是否有解,无解则直接输出(判断的时候记录冲突,conflict数组,这里还不是特别明白!!!

4、把每个强连通子图收缩成单个节点,根据原图关系反向构造一个有向无环图

5、对新图进行拓扑排序,并进行染色(color数组)

6、记录哪些点赋值为“0”,哪些赋值为“1”

7、输出:

#include <stdio.h>
#include <string.h>
#define cle(a) memset(a,0,sizeof(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define N 1005*2
struct node{//保存时间,x和y为起始和结束的时间(以分钟计)
	int x,y,last;
}p[N];
struct edge{
	int y,next;
}e[N*N*2],e_t[N*N*2];
int first[N],first_t[N],dfn[N],low[N],stack[N],strong[N],d[N];
int conflict[N],color[N],res[N],q[N];
int n,id,top,tops,top_t,num;
void init(){
	tops = -1;
	id = top = top_t = num = 0;
	memset(first,-1,sizeof(first));
	memset(first_t,-1,sizeof(first_t));
	memset(dfn,-1,sizeof(dfn));
	cle(strong);
	cle(d);
	cle(color);
	cle(res);
}
void add(int x,int y){
	e[top].y = y;
	e[top].next = first[x];
	first[x] = top++;
}
void add2(int x,int y){//缩点后建立的新图,为拓扑排序
	e_t[top_t].y = y;
	e_t[top_t].next = first_t[x];
	first_t[x] = top_t++;
}
int test_cross(int i,int j,int a,int b){//判断两个时间段是否冲突,0表示开始阶段,1表示结束阶段
	int x1,y1,x2,y2;
	if(!a){
		x1 = p[i].x;
		y1 = p[i].x + p[i].last;
	}else{
		x1 = p[i].y - p[i].last;
		y1 = p[i].y;
	}
	if(!b){
		x2 = p[j].x;
		y2 = p[j].x + p[j].last;
	}else{
		x2 = p[j].y - p[j].last;
		y2 = p[j].y;
	}
	return (x1<y2) && (x2<y1);
}
void tarjan(int x){
	int i,y;
	dfn[x] = low[x] = ++id;
	stack[++tops] = x;
	for(i = first[x];i!=-1;i=e[i].next){
		y = e[i].y;
		if(dfn[y] == -1){
			tarjan(y);
			low[x] = min(low[x],low[y]);
		}else if(!strong[y])
			low[x] = min(low[x],dfn[y]);
	}
	if(dfn[x] == low[x]){
		num++;
		do{
			strong[stack[tops]] = num;
		}while(stack[tops--]!=x);
	}
}
void create_graph(){//建立新图
	int i,j;
	for(i = 1;i<=n<<1;i++)
		for(j = first[i];j!=-1;j=e[j].next)
			if(strong[i] != strong[e[j].y]){
				d[strong[i]]++;
				add2(strong[e[j].y],strong[i]);
			}
}
void topsort(){//求拓扑排序,并染色
	int i,now,front,rear;
	front = rear = -1;
	for(i = 1;i<=num;i++)
		if(!d[i])
			q[++rear] = i;
	while(front < rear){
		now = q[++front];
		if(!color[now]){
			color[now] = 1;
			color[conflict[now]] = -1;
		}
		for(i = first_t[now];i!=-1;i=e_t[i].next){
			--d[e_t[i].y];
			if(!d[e_t[i].y])
				q[++rear] = e_t[i].y;
		}
	}
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d",&n)!=EOF){
		int i,j,h1,h2,m1,m2;
		init();
		for(i = 1;i<=n;i++){//读入,并将时间转化为分钟
			scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&p[i].last);
			p[i].x = h1*60+m1;
			p[i].y = h2*60+m2;
		}
		for(i = 1;i<n;i++)//分别判断是否冲突,建图tarjan
			for(j = i+1;j<=n;j++){
				if(test_cross(i,j,0,0)){
					add(i,j+n);add(j,i+n);
				}
				if(test_cross(i,j,0,1)){
					add(i,j);add(j+n,i+n);
				}
				if(test_cross(i,j,1,0)){
					add(i+n,j+n);add(j,i);
				}
				if(test_cross(i,j,1,1)){
					add(i+n,j);add(j+n,i);
				}
			}
		for(i = 1;i<=n<<1;i++)//tarjan
			if(dfn[i] == -1)
				tarjan(i);
		for(i = 1;i<=n;i++){
			if(strong[i] == strong[i+n])
				break;
			conflict[strong[i]] = strong[i+n];//记录冲突????
			conflict[strong[i+n]] = strong[i];
		}
		if(i>n){
			printf("YES\n");
			create_graph();
			topsort();
			for(i = 1;i<=n;i++)
				if(color[strong[i]] == 1)//所在强连通分量染色为1的点表示取到
					res[i] = 1;
			for(i = 1;i<=n;i++)
				if(res[i] == 1){//表示婚礼开始阶段
					printf("%02d:%02d ",p[i].x/60,p[i].x%60);
					printf("%02d:%02d\n",(p[i].x+p[i].last)/60,(p[i].x+p[i].last)%60);
				}else{//婚礼结束阶段
					printf("%02d:%02d ",(p[i].y-p[i].last)/60,(p[i].y-p[i].last)%60);
					printf("%02d:%02d\n",p[i].y/60,p[i].y%60);
				}
		}else
			printf("NO\n");
	}
	return 0;
}


你可能感兴趣的:(poj 3683 2-SAT (牧师能参加所有的婚礼仪式吗))