POJ 3683 Priest John's Busiest Day

题意:

安排婚礼。有 n 项婚礼,每项婚礼的举办时间为 s,结束时间为 e,持续时间为 d,婚礼举办开始的时间还有一个特殊的要求必须在(s~s+d)或者(e-d~e),然后输出安排的方式。

分析:

将 n 项婚礼安排为两组,第一组在 s 的时间开始举办,第二组在 e-d 的时间开始举办,然后这很显然和 2-sat 求解的类型相似,用 2-sat 求解。

在处理矛盾建边的过程中,我开始想用 2 * n 个点建立关系,然后 if,else 写个没完,还搞的稀里糊涂。之后,我发现用 4 * n 个节点建边更简单,2 * n 点表示婚礼开始时间,其他2 * n 个点为取反,其中,i 与 i + n 中,只能取一个,关系为 i xor i + n = 1;此外根据时间冲突,产生的矛盾关系,则 时间段 a && 时间段 b = 0。

最后求完强连通以后,要需要输出结果,需要反向建边,并拓扑排序。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 11000;
const int inf = 1000000000;
struct T{
	int S, E;
}tim[maxn];
bool cmp( T a, T b )
{
	if( a.S < b.E && a.S > b.S && a.E > b.E ||
		a.S < b.S && a.E > b.S && a.E < b.E ||
		a.S <= b.S && a.E >= b.E ||
		a.S >= b.S && a.E <= b.E )
		return true;
	return false;
}
vector<int>edge[maxn];
int n, m;
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
void tarjan( int u )
{
	int i, v, t, size;
	low[u] = dfn[u] = tmpdfn++;
	st[top++] = u;
	inst[u] = 1;
	size = edge[u].size();
	for( i = 0; i < size; i++ )
	{
		v = edge[u][i];
		if( dfn[v] == -1 )
		{
			tarjan( v );
			low[u] = min( low[u], low[v] );
		}
		else if( inst[v] )low[u] = min( low[u], dfn[v] );
	}
	if( dfn[u] == low[u] )
	{
		do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
		scnt++;
	}
}
bool SCC()
{
	int i;
	top = 0;
	tmpdfn = scnt = 1;
	memset( dfn, -1, sizeof(dfn) );
	memset( inst, 0, sizeof(inst) );
	for( i = 1; i <= n * 2; i++ )if( dfn[i] == -1 )tarjan( i );
	for( i = 1; i <= n; i++ )if( belong[i] == belong[i + n] )return false;
	return true;
}
int opp[maxn], in[maxn], color[maxn];
vector<int>arc[maxn];
queue<int>q;
void rebuild()
{
	memset( in, 0, sizeof(in) );
	int u, v, i, size;
	for( i = 1; i <= n; i++ )
	{
		opp[belong[i]] = belong[i + n];
		opp[belong[i + n]] = belong[i];
	}
	for( i = 1; i <= n * 2; i++ )arc[i].clear();
	for( u = 1; u <= n * 2; u++ )
	{
		size = edge[u].size();
		for( i = 0; i < size; i++ )
		{
			v = edge[u][i];
			if( belong[u] != belong[v] )
			{
				arc[belong[v]].push_back( belong[u] );
				in[belong[u]]++;
			}
		}
	}
}
void topusort()
{
	int i, j, u, v, size;
	while( !q.empty() )q.pop();
	memset( color, 0, sizeof(color) );
	for( i = 1; i < scnt; i++ )if( !in[i] )
		q.push( i );
	while( !q.empty() )
	{
		u = q.front();	q.pop();
		if( !color[u] )color[u] = 1, color[opp[u]] = 2;
		size = arc[u].size();
		for( i = 0; i < size; i++ )
		{	
			v = arc[u][i];
			in[v]--;
			if( !in[v] )q.push( v );
		}
	}
}
int main()
{
	int i, j, u, v, s, e, dif;
	while( ~scanf( "%d", &n ) )
	{
		for( i = 1; i <= n * 4; i++ )edge[i].clear();
		for( i = 1; i <= n; i++ )
		{
			int x, y, xx, yy;
			scanf( "%d:%d%d:%d%d", &x, &y, &xx, &yy, &dif );
			s = x * 60 + y;
			e = xx * 60 + yy;
			tim[i].S = s,	tim[i].E = s + dif;
			tim[i + n].S = e - dif,	tim[i + n].E = e; 
		}
		for( i = 1; i <= n; i++ )
		{
			u = i;	v = i + n;
			edge[u].push_back( v + 2 * n );
			edge[v].push_back( u + 2 * n );
			edge[v + 2 * n].push_back( u );
			edge[u + 2 * n].push_back( v );
		}
		n *= 2;
		for( i = 1; i <= n; i++ )
			for( j = i + 1; j <= n; j++ )if( cmp( tim[i], tim[j] ) )
			{
				edge[i].push_back( j + n );
				edge[j].push_back( i + n );
			}
		if( SCC() )
		{
			puts( "YES" );
			rebuild();
			topusort();
			n /= 2;
			for( i = 1; i <= n; i++ )
				if( color[belong[i]] == 1 )printf( "%02d:%02d %02d:%02d\n", tim[i].S / 60, tim[i].S % 60, tim[i].E / 60, tim[i].E % 60 );
				else printf( "%02d:%02d %02d:%02d\n", tim[i + n].S / 60, tim[i + n].S % 60, tim[i + n].E / 60, tim[i + n].E % 60 );
		}
		else
			puts( "NO" );
	}
	return 0;
}
/*
3
08:00 09:00 30
08:15 09:00 20
08:30 08:50 1

3
08:00 09:00 30
08:15 09:00 10
08:30 08:50 10

6
08:00 09:00 30
08:15 09:00 10
08:30 08:50 10
08:00 10:00 20
09:00 09:20 10
09:00 09:40 20

5
08:00 09:00 30
08:15 09:00 10
08:30 08:50 10
08:00 10:00 20
09:00 09:20 10

9
08:00 09:00 30
08:15 09:00 10
08:30 08:50 10
08:00 10:00 20
09:00 09:20 10
09:00 09:40 20
10:00 10:30 10
10:20 10:30 10
10:10 10:20 10




*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值