1. 程式人生 > 其它 >371. 牧師約翰最忙碌的一天

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\)

\(T_i-D_i \sim T_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]\)

或者 \([t_i-d_i,t_i],[s_j,s_j+d_j]\) 或者 \([t_i-d_i,t_i],[t_j-d_j,t_j]\),以 \([s_i,s_i+d_i],[s_j,s_j+d_j]\) 這兩個區間重疊為例,即不能存在一種邏輯 \(\neg x_i\) 為真並且 \(\neg x_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;
}