题目链接
牧师约翰在 9 月 1 日这天非常的忙碌。
有 N 对情侣在这天准备结婚,每对情侣都预先计划好了婚礼举办的时间,其中第 i 对情侣的婚礼从时刻 Si 开始,到时刻 Ti 结束。
婚礼有一个必须的仪式:站在牧师面前聆听上帝的祝福。
这个仪式要么在婚礼开始时举行,要么在结束时举行。
第 i 对情侣需要 D_i 分钟完成这个仪式,即必须选择 Si∼Si+Di 或 Ti−Di∼Ti 两个时间段之一。
牧师想知道他能否满足每场婚礼的要求,即给每对情侣安排Si∼Si+Di 或 Ti−Di∼Ti,使得这些仪式的时间段不重叠。
若能满足,还需要帮牧师求出任意一种具体方案。
注意,约翰不能同时主持两场婚礼,且 所有婚礼的仪式均发生在 9 月 1 日当天。
如果一场仪式的结束时间与另一场仪式的开始时间相同,则不算重叠。
例如:一场仪式安排在 08:00∼09:00,另一场仪式安排在 09:00∼10:00,则不认为两场仪式出现重叠。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含 Si,Ti,Di,其中 Si 和 Ti 是 hh:mm 形式。
输出格式
第一行输出能否满足,能则输出 YES
,否则输出 NO
。
接下来 N 行,每行给出一个具体时间段安排。
数据范围
1≤N≤1000
输入样例:
输出样例:
解题思路
2-SAT
由于 [s,s+d] 和 [t−d,t] 这两个区间只能选择一个,不妨看成一个命题,即令 ¬x 表示 [s,s+d],x 表示 [t−d,t],当两个区间重叠时,存在这样两个区间 [si,si+di],[sj,sj+dj] 或者 [si,si+di],[tj−dj,tj] 或者 [ti−di,ti],[sj,sj+dj] 或者 [ti−di,ti],[tj−dj,tj],以 [si,si+di],[sj,sj+dj] 这两个区间重叠为例,即不能存在一种逻辑 ¬xi 为真并且 ¬xj
为真,故当 ¬xi 为真时,必然有 ¬xj 为假,即有 xi∪xj,则可将该问题转化为 2-SAT 问题
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=2005,M=N*N;
int n;
int h[N],ne[M],e[M],idx;
int dfn[N],low[N],timestamp,scc_cnt,stk[N],top,id[N];
bool in_stk[N];
struct A
{
int s,t,d;
}a[N/2];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool over_lap(int a,int b,int c,int d)
{
return b>c&&d>a;
}
void tarjan(int x)
{
dfn[x]=low[x]=++timestamp;
stk[++top]=x,in_stk[x]=true;
for(int i=h[x];~i;i=ne[i])
{
int y=e[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(in_stk[y])
low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
int y;
scc_cnt++;
do
{
y=stk[top--];
in_stk[y]=false;
id[y]=scc_cnt;
}while(y!=x);
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int hhs,mms,hht,mmt,d;
scanf("%d:%d %d:%d %d",&hhs,&mms,&hht,&mmt,&d);
a[i]={hhs*60+mms,hht*60+mmt,d};
}
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
{
if(over_lap(a[i].s,a[i].s+a[i].d,a[j].s,a[j].s+a[j].d))add(i<<1|1,j<<1),add(j<<1|1,i<<1);
if(over_lap(a[i].t-a[i].d,a[i].t,a[j].s,a[j].s+a[j].d))add(i<<1,j<<1),add(j<<1|1,i<<1|1);
if(over_lap(a[i].s,a[i].s+a[i].d,a[j].t-a[j].d,a[j].t))add(i<<1|1,j<<1|1),add(j<<1,i<<1);
if(over_lap(a[i].t-a[i].d,a[i].t,a[j].t-a[j].d,a[j].t))add(i<<1,j<<1|1),add(j<<1,i<<1|1);
}
for(int i=0;i<2*n;i++)
if(!dfn[i])tarjan(i);
for(int i=0;i<n;i++)
if(id[i<<1]==id[i<<1|1])
{
puts("NO");
return 0;
}
puts("YES");
for(int i=0;i<n;i++)
if(id[i<<1|1]>id[i<<1])
printf("%02d:%02d %02d:%02d\n",(a[i].t-a[i].d)/60,(a[i].t-a[i].d)%60,a[i].t/60,a[i].t%60);
else
printf("%02d:%02d %02d:%02d\n",a[i].s/60,a[i].s%60,(a[i].s+a[i].d)/60,(a[i].s+a[i].d)%60);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!