POJ 3683 Priest John's Busiest Day

题目大意:

题目链接

注释代码:

/*
 * Problem ID : POJ 3683 Priest John's Busiest Day
 * Author     : Lirx.t.Una
 * Language   : C++
 * Run Time   : 266 ms
 * Run Memory : 23832 KB
*/

#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>

//思路:
//如果x表示第x号婚礼在开始时举行而-x表示在结束时举行
//比如x∧y就表示婚礼x和y都在开始时举行
//但是如果从题给的事实中得出x∧y不成立(即两场婚礼会有冲突)
//则不冲突的情况只可能发生在-(x∧y)中了,如果该表达式也无法成立的话则牧师就不能完成任务了

//由此可见算法的步骤为:
//1:找出所有使i∧j不成立的(i, j)对子,其中i、j为布尔文字,i、j可以为x也可以为-x
//2:然后将i∧j化为-i∨-j形式(为了构造主合取范式),最后便是求布尔方程(a1∨b1)∧(a2∨b2)...(an^bn) = true
//3:对于每个布尔子句i∨j,可以化为蕴含表达式-i->j ∧ -j->i,这样主合取范式就全部化为蕴含式的∧
//4:对i和-i进行编号,然后对于每一对i->j做一条有向边构图进行强连通分量分解
//5:如果存在i和-i在同一个强连通分量中则布尔方程无解,否则就有解
//6:对于有解的情况,如果要求某个布尔变量x的取值
    //如果x所在的强连通分量在缩点图中的拓扑排序在-x之后的强连通分量之后就去true否则就取false

//婚礼的最大数量
#define	MAXN	1000
#define	MAXNN	2000

using namespace std;

short	s[MAXN + 1];//婚礼开始时间
short	t[MAXN + 1];//截止时间
short	d[MAXN + 1];//祈祷用时

//正向图和反向图
//其中某个点i表示i号婚礼在开始时进行,则i + n表示在i在截止时进行两种布尔状态
//其中n为婚礼数量
int		head[MAXNN + 1];
int		to[MAXNN * MAXNN + 1];
int		nxt[MAXNN * MAXNN + 1];
int		_head[MAXNN + 1];
int		_to[MAXNN * MAXNN + 1];
int		_nxt[MAXNN * MAXNN + 1];
int		e;

short	blg[MAXNN + 1];
bool	vis[MAXNN + 1];
int		nb;

stack<short>	stk;

inline int
min( int a, int b ) {

	return a < b ? a : b;
}

inline int
max( int a, int b ) {

	return a > b ? a : b;
}

inline void
addarc( int u, int v ) {

	to[e]   = v;
	nxt[e]  = head[u];
	head[u] = e;

	_to[e]   = u;
	_nxt[e]  = _head[v];
	_head[v] = e++;
}

void
dfs(int u) {

	int		v, i;

	vis[u] = true;
	for ( i = head[u]; i; i = nxt[i] )
		if ( !vis[ v = to[i] ] )
			dfs(v);
	stk.push(u);
}

void
_dfs(int u) {

	int		v, i;

	vis[u] = true;
	blg[u] = nb;

	for ( i = _head[u]; i; i = _nxt[i] )
		if ( !vis[ v = _to[i] ] )
			_dfs(v);
}

void
kosa(int nn) {

	int		i;

	for ( i = 1; i <= nn; i++ )
		if ( !vis[i] )
			dfs(i);

	memset(vis, 0, sizeof(vis));
	for ( nb = 0; !stk.empty(); stk.pop() )
		if ( !vis[ i = stk.top() ] )
			++nb, _dfs(i);
}

int
main() {

	int		n, nn;//婚礼数和布尔状态数
	int		hh, mm, dd;//临时接受小时、分钟、用时

	int		i, j;
	int		tmp;

	scanf("%d", &n);
	nn = n << 1;
	e  = 1;

	for ( i = 1; i <= n; i++ ) {
	
		scanf("%d:%d", &hh, &mm);
		s[i]  = hh * 60 + mm;//将时间转化为以分钟为单位

		scanf("%d:%d", &hh, &mm);
		t[i]  = hh * 60 + mm;

		scanf("%d", &dd);
		d[i] = dd;
	}

	for ( i = 1; i <= n; i++ )
		for ( j = i + 1; j <= n; j++ ) {//找出所有^表达式不成立的对子
		
			//i∧j ≡ -i∨-j ≡ i->-j∧j->-i
			if ( min( s[i] + d[i], s[j] + d[j] ) > max( s[i], s[j] ) ) {//都在开始时冲突

				addarc( i, j + n );
				addarc( j, i + n );
			}

			//i∧-j ≡ -i∨j ≡ i->j∧-j->-i
			if ( min( s[i] + d[i], t[j] ) > max( s[i], t[j] - d[j] ) ) {//开始和截止冲突
			
				addarc( i, j );
				addarc( j + n, i + n );
			}

			//-i∧j ≡ i∨-j ≡ -i->-j∧j->i
			if ( min( t[i], s[j] + d[j] ) > max( t[i] - d[i], s[j] ) ) {//截止和开始冲突
			
				addarc( i + n, j + n );
				addarc( j, i );
			}

			//-i∧-j ≡ i∨j ≡ -i->j∧-j->i
			if ( min( t[i], t[j] ) > max( t[i] - d[i], t[j] - d[j] ) ) {//都在截止时冲突
			
				addarc( i + n, j );
				addarc( j + n, i );
			}
		}

	kosa(nn);

	for ( i = 1; i <= n; i++ )
		if ( blg[i] == blg[i + n] ) {//自己和自己的非在同一个SCC中则布尔方程误解
		
			puts("NO");
			return 0;
		}

	puts("YES");
	for ( i = 1; i <= n; i++ )
		if ( blg[i] > blg[i + n] ) {//自己比自己的非的强连通分量的拓扑排序序号要大则取true(就表示在开始时举办)

			tmp = s[i] + d[i];
			printf("%02d:%02d %02d:%02d\n",
				   s[i] / 60, s[i] % 60,
				   tmp / 60, tmp %  60);
		}
		else {//否则就false的情况(在截止时举办)

			tmp = t[i] - d[i];
			printf("%02d:%02d %02d:%02d\n",
				   tmp / 60, tmp % 60,
				   t[i] / 60, t[i] %  60);
		}

	return 0;
}
无注释代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>

#define	MAXN	1000
#define	MAXNN	2000

using namespace std;

short	s[MAXN + 1];
short	t[MAXN + 1];
short	d[MAXN + 1];

int		head[MAXNN + 1];
int		to[MAXNN * MAXNN + 1];
int		nxt[MAXNN * MAXNN + 1];
int		_head[MAXNN + 1];
int		_to[MAXNN * MAXNN + 1];
int		_nxt[MAXNN * MAXNN + 1];
int		e;

short	blg[MAXNN + 1];
bool	vis[MAXNN + 1];
int		nb;

stack<short>	stk;

inline int
min( int a, int b ) {

	return a < b ? a : b;
}

inline int
max( int a, int b ) {

	return a > b ? a : b;
}

inline void
addarc( int u, int v ) {

	to[e]   = v;
	nxt[e]  = head[u];
	head[u] = e;

	_to[e]   = u;
	_nxt[e]  = _head[v];
	_head[v] = e++;
}

void
dfs(int u) {

	int		v, i;

	vis[u] = true;
	for ( i = head[u]; i; i = nxt[i] )
		if ( !vis[ v = to[i] ] )
			dfs(v);
	stk.push(u);
}

void
_dfs(int u) {

	int		v, i;

	vis[u] = true;
	blg[u] = nb;

	for ( i = _head[u]; i; i = _nxt[i] )
		if ( !vis[ v = _to[i] ] )
			_dfs(v);
}

void
kosa(int nn) {

	int		i;

	for ( i = 1; i <= nn; i++ )
		if ( !vis[i] )
			dfs(i);

	memset(vis, 0, sizeof(vis));
	for ( nb = 0; !stk.empty(); stk.pop() )
		if ( !vis[ i = stk.top() ] )
			++nb, _dfs(i);
}

int
main() {

	int		n, nn;
	int		hh, mm, dd;

	int		i, j;
	int		tmp;

	scanf("%d", &n);
	nn = n << 1;
	e  = 1;

	for ( i = 1; i <= n; i++ ) {
	
		scanf("%d:%d", &hh, &mm);
		s[i]  = hh * 60 + mm;

		scanf("%d:%d", &hh, &mm);
		t[i]  = hh * 60 + mm;

		scanf("%d", &dd);
		d[i] = dd;
	}

	for ( i = 1; i <= n; i++ )
		for ( j = i + 1; j <= n; j++ ) {
		
			if ( min( s[i] + d[i], s[j] + d[j] ) > max( s[i], s[j] ) ) {

				addarc( i, j + n );
				addarc( j, i + n );
			}

			if ( min( s[i] + d[i], t[j] ) > max( s[i], t[j] - d[j] ) ) {
			
				addarc( i, j );
				addarc( j + n, i + n );
			}

			if ( min( t[i], s[j] + d[j] ) > max( t[i] - d[i], s[j] ) ) {
			
				addarc( i + n, j + n );
				addarc( j, i );
			}

			if ( min( t[i], t[j] ) > max( t[i] - d[i], t[j] - d[j] ) ) {
			
				addarc( i + n, j );
				addarc( j + n, i );
			}
		}

	kosa(nn);

	for ( i = 1; i <= n; i++ )
		if ( blg[i] == blg[i + n] ) {
		
			puts("NO");
			return 0;
		}

	puts("YES");
	for ( i = 1; i <= n; i++ )
		if ( blg[i] > blg[i + n] ) {

			tmp = s[i] + d[i];
			printf("%02d:%02d %02d:%02d\n",
				   s[i] / 60, s[i] % 60,
				   tmp / 60, tmp %  60);
		}
		else {

			tmp = t[i] - d[i];
			printf("%02d:%02d %02d:%02d\n",
				   tmp / 60, tmp % 60,
				   t[i] / 60, t[i] %  60);
		}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值