371. 牧師約翰最忙碌的一天
題目連結
371. 牧師約翰最忙碌的一天
牧師約翰在 \(9\) 月 \(1\) 日這天非常的忙碌。
有 \(N\) 對情侶在這天準備結婚,每對情侶都預先計劃好了婚禮舉辦的時間,其中第 \(i\) 對情侶的婚禮從時刻 \(S_i\) 開始,到時刻 \(T_i\) 結束。
婚禮有一個必須的儀式:站在牧師面前聆聽上帝的祝福。
這個儀式要麼在婚禮開始時舉行,要麼在結束時舉行。
第 \(i\) 對情侶需要 \(D\_i\) 分鐘完成這個儀式,即必須選擇 \(S_i \sim S_i+D_i\) 或 \(T_i-D_i \sim T_i\) 兩個時間段之一。
牧師想知道他能否滿足每場婚禮的要求,即給每對情侶安排\(S_i \sim S_i+D_i\)
若能滿足,還需要幫牧師求出任意一種具體方案。
注意,約翰不能同時主持兩場婚禮,且 所有婚禮的儀式均發生在 \(9\) 月 \(1\) 日當天。
如果一場儀式的結束時間與另一場儀式的開始時間相同,則不算重疊。
例如:一場儀式安排在 \(08:00 \sim 09:00\),另一場儀式安排在 \(09:00 \sim 10:00\),則不認為兩場儀式出現重疊。
輸入格式
第一行包含整數 \(N\)。
接下來 \(N\) 行,每行包含 \(S_i,T_i,D_i\),其中 \(S_i\) 和 \(T_i\) 是 \(hh:mm\) 形式。
輸出格式
第一行輸出能否滿足,能則輸出 YES
,否則輸出 NO
。
接下來 \(N\) 行,每行給出一個具體時間段安排。
資料範圍
\(1 \le N \le 1000\)
輸入樣例:
2
08:00 09:00 30
08:15 09:00 20
輸出樣例:
YES
08:00 08:30
08:40 09:00
解題思路
2-SAT
由於 \([s,s+d]\) 和 \([t-d,t]\) 這兩個區間只能選擇一個,不妨看成一個命題,即令 \(\neg x\) 表示 \([s,s+d]\),\(x\) 表示 \([t-d,t]\),當兩個區間重疊時,存在這樣兩個區間 \([s_i,s_i+d_i],[s_j,s_j+d_j]\) 或者 \([s_i,s_i+d_i],[t_j-d_j,t_j]\)
為真,故當 \(\neg x_i\) 為真時,必然有 \(\neg x_j\) 為假,即有 \(x_i\cup x_j\),則可將該問題轉化為 2-SAT 問題
- 時間複雜度:\((n^2)\)
程式碼
// Problem: 牧師約翰最忙碌的一天
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/373/
// Memory Limit: 128 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#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;
}