POJ-3683 Priest John's Busiest Day(2-SAT问题)

本文探讨了一个有趣的2-SAT问题实例——如何安排牧师为多对新人主持婚礼仪式,确保不发生时间冲突。通过构建图模型并采用Tarjan算法寻找强连通分量,最终实现有效的时间调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

一个小镇里面只有一个牧师,现在有些新人要结婚,需要牧师分别去主持一个仪式,给出每个婚礼的开始时间 s 和结束时间 t ,还有该场婚礼的仪式需要进行的时间d(每对新人需要的时间长短可能不同),仪式只能在婚礼刚开始或者剩余d时间结束时进行,即仪式只能在[s, s+d]时间段内或者在[t-d, d]时间段内进行。问能否给出一种安排,使牧师能够完成所有新人婚礼的仪式,如果可以,输出一种安排。

思路:

明显的2-SAT问题,每场婚礼只有两个举行仪式的时间,然后每个元素之间的冲突就是该场婚礼的一个时间元素与其它婚礼建的时间元素重叠(另外,认为08:00-08:30与08:30-09:00也是重叠的),图的时候需要注意。之后便可以利用两个模板进行求解了。


Code:

#include <algorithm>
#include <string.h>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 1005*2;
const int maxm = maxn*maxn;
struct node
{
	int u, v, next;
} edge[maxm], new_edge[maxm];
int head[maxn];
int n, no, new_no;
char s[maxn][2][10];
int tim[maxn][2], limit[maxn];
int index, top;
int dfn[maxn], low[maxn], stack[maxn], vis[maxn];
int cnt;
int belong[maxn];
queue<int> q;
int opp[maxn], deg[maxn], color[maxn];
inline void init()
{
	no = 0;
	memset(head, -1, sizeof head);
	index = top = 0;
	memset(dfn, 0, sizeof dfn);
	memset(vis, 0, sizeof vis);
	new_no = 0;
	memset(deg, 0, sizeof deg);
	memset(color, 0, sizeof color);
}
inline void add(int u, int v)
{
	edge[no].u = u, edge[no].v = v;
	edge[no].next = head[u]; head[u] = no++;
}
inline void new_add(int u, int v)
{
	new_edge[new_no].u = u, new_edge[new_no].v = v;
	new_edge[new_no].next = head[u]; head[u] = new_no++;
}
void mapping()
{
	int hh, mm, minn, maxx;
	scanf("%d", &n); init();
	for(int i = 0; i < n; ++i)
	{
		scanf("%s %s %d", s[i][0], s[i][1], &limit[i]);
		hh = (s[i][0][0]-'0')*10+(s[i][0][1]-'0');
		mm = (s[i][0][3]-'0')*10+(s[i][0][4]-'0');
		tim[i][0] = hh*60+mm;
		hh = (s[i][1][0]-'0')*10+(s[i][1][1]-'0');
		mm = (s[i][1][3]-'0')*10+(s[i][1][4]-'0');
		tim[i][1] = hh*60+mm;
	}
	for(int i = 0; i < n; ++i)
	for(int j = i+1; j < n; ++j)
	{
		minn = min(tim[i][0], tim[j][0]);
		maxx = max(tim[i][0]+limit[i], tim[j][0]+limit[j]);
		if(maxx-minn < limit[i]+limit[j])
		{
			add(i*2, j*2+1);
			add(j*2, i*2+1);
		}
		minn = min(tim[i][0], tim[j][1]-limit[j]);
		maxx = max(tim[i][0]+limit[i], tim[j][1]);
		if(maxx-minn < limit[i]+limit[j])
		{
			add(i*2, j*2);
			add(j*2+1, i*2+1);
		}
		minn = min(tim[i][1]-limit[i], tim[j][1]-limit[j]);
		maxx = max(tim[i][1], tim[j][1]);
		if(maxx-minn < limit[i]+limit[j])
		{
			add(i*2+1, j*2);
			add(j*2+1, i*2);
		}
		minn = min(tim[i][1]-limit[i], tim[j][0]);
		maxx = max(tim[i][1], tim[j][0]+limit[j]);
		if(maxx-minn < limit[i]+limit[j])
		{
			add(i*2+1, j*2+1);
			add(j*2, i*2);
		}
	}
}
void tarjan(int cur)
{
	dfn[cur] = low[cur] = ++index;
	stack[++top] = cur; vis[cur] = 1;
	for(int k = head[cur]; k != -1; k = edge[k].next)
	{
		if(!dfn[edge[k].v])
		{
			tarjan(edge[k].v);
			low[cur] = min(low[cur], low[edge[k].v]);
		}
		else if(vis[edge[k].v])
		{
			low[cur] = min(low[cur], dfn[edge[k].v]);
		}
	}
	if(dfn[cur] == low[cur])
	{
		++cnt;
		do
		{
			vis[stack[top]] = 0;
			belong[stack[top]] = cnt;
			--top;
		}
		while(stack[top+1] != cur);
	}
}
void topsort()
{
	memset(head, -1, sizeof head);
	for(int i = 0; i < no; ++i)
	{
		int u = edge[i].u, v = edge[i].v;
		if(belong[u] == belong[v]) continue;
		new_add(belong[v], belong[u]);
		++deg[belong[u]];
	}
	while(!q.empty()) q.pop();
	for(int i = 1; i <= cnt; ++i) if(!deg[i]) q.push(i);
	while(!q.empty())
	{
		int u = q.front(); q.pop();
		if(!color[u]) color[u] = 1, color[opp[u]] = -1;
		int k = head[u];
		while(k != -1)
		{
			if(--deg[new_edge[k].v] == 0) q.push(new_edge[k].v);
			k = new_edge[k].next;
		}
	}
}
void print()
{
	puts("YES");
	for(int i = 0; i < n; ++i)
	{
		if(color[belong[i*2]] == 1)
		{
			int mm = tim[i][0]+limit[i];
			printf("%s %02d:%02d\n", s[i][0], mm/60, mm%60);
		}
		if(color[belong[i*2+1]] == 1)
		{
			int mm = tim[i][1]-limit[i];
			printf("%02d:%02d %s\n", mm/60, mm%60, s[i][1]);
		}
	}
}
void solve()
{
	for(int i = 0; i < n*2; ++i)
		if(!dfn[i]) tarjan(i);
	for(int i = 0; i < n; ++i)
	{
		if(belong[i*2] == belong[i*2+1])
		{
			puts("NO");
			return ;	
		}
		else
		{
			opp[belong[i*2]] = belong[i*2+1];
			opp[belong[i*2+1]] = belong[i*2];
		}
	}
	topsort();
	print();
}
int main()
{
	mapping();
	solve();
	return 0;
}

继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值