题目大意:一个牧师要给n个婚礼进行仪式,给定婚礼开始的时间和结束的时间,仪式可以在婚礼开始的时候进行,也可以在婚礼结束前进行,问牧师能否完成所有的婚礼仪式并输出方案。
还是经典的2-SAT模型,为什么呢,我们来分析:
首先,一场婚礼有两个时间可以选择,刚好对应2-SAT的两个对称点,其次我们需要满足所有的仪式进行,所以就出现了约束条件,那么2-SAT可以很好的解决他们。建图的话,如果两个时间段
i
,
j
i,j
i,j有重叠的话那么
i
→
j
′
,
j
→
i
′
i\rightarrow j', j\rightarrow i'
i→j′,j→i′
再补充一个不需要Topsort的易实现做法,我们知道Tarjan的本质是一个DFS,实际上构建了一颗DFS树。那么他在回溯的时候自底向上求取强连通分量,所以Tarjan算法下DFS树中越深的节点得到的强连通分量编号会越小,所以他本身已经满足了缩点后的有向无环图”自底向上“的拓扑序。 那么我们可以直接比较点i和他的对应点的强连通分量编号来染色(给点和对应点染上不同的颜色,注意自己明确什么颜色对应选/不选)
注意这题的输入输出处理比较麻烦。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2100;
const int maxm=4000000;
struct AKCqhzdy
{
int x,y,next;
}a[maxm]; int len,last[maxn];
int n;
void ins(int x,int y)
{
len++; a[len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
int id,top,scc;
int dfn[maxn],low[maxn];
int sta[maxn]; bool insta[maxn];
int bl[maxn];
void tarjan(int x)
{
sta[++top]=x; insta[x]=true;
low[x]=dfn[x]=++id;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(dfn[y]==-1)
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(insta[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
scc++; int i;
do{
i=sta[top--];
insta[i]=false;
bl[i]=scc;
}while(i!=x);
}
}
struct OrzAKC
{
int l,r,c;
}t[maxn];
struct Rose_max
{
int x,y,next;
}b[maxm]; int last2[maxn],len2=0;
void ins2(int x,int y)
{
len2++; b[len2].x=x; b[len2].y=y;
b[len2].next=last2[x]; last2[x]=len2;
}
int pairs[maxm],col[maxm];
void build()
{
memset(last,0,sizeof(last)); len=0;
for(int i=1;i<n;i++)
{
int li1=t[i].l,ri1=t[i].l+t[i].c,li2=t[i].r-t[i].c,ri2=t[i].r;
for(int j=i+1;j<=n;j++)
{
int lj1=t[j].l,rj1=t[j].l+t[j].c,lj2=t[j].r-t[j].c,rj2=t[j].r;
if(li1<rj1 && lj1<ri1)
{
ins(2*i,2*j+1);
ins(2*j,2*i+1);
}
if(li1<rj2 && lj2<ri1)
{
ins(2*i,2*j);
ins(2*j+1,2*i+1);
}
if(li2<rj1 && lj1<ri2)
{
ins(2*i+1,2*j+1);
ins(2*j,2*i);
}
if(li2<rj2 && lj2<ri2)
{
ins(2*j+1,2*i);
ins(2*i+1,2*j);
}
}
}
}
bool solve()
{
int i;
memset(dfn,-1,sizeof(dfn));
memset(low,0,sizeof(low));
memset(bl,-1,sizeof(bl));
memset(insta,false,sizeof(insta));
scc=id=top=0;
for(i=1;i<=2*n;i++)
if(dfn[i]==-1)
tarjan(i);
for(i=1;i<=n;i++)
if(bl[2*i]==bl[2*i+1])
return false;
else
{
pairs[bl[2*i]]=bl[2*i+1];
pairs[bl[2*i+1]]=bl[2*i];
}
return true;
}
void rebuild()
{
memset(last2,0,sizeof(last2)); len2=0;
for(int i=1;i<=len;i++)
{
if(bl[a[i].x] != bl[a[i].y] )
ins2( bl[a[i].x] , bl[a[i].y]);
}
}
void paint(int x)
{
insta[x]=true;
for(int k=last2[x];k;k=b[k].next)
{
int y=b[k].y;
if(!insta[y]) paint(y);
}
if(col[x]==-1)
{
col[x]=1;
col[pairs[x]]=0;
}
}
void topsort()
{
memset(col,-1,sizeof(col));
memset(insta,false,sizeof(insta));
for(int i=1;i<=scc;i++)
if(!insta[i])
paint(i);
}
void print()
{
int i;
for (i=1;i<=n;i++)
{
if (col[bl[2*i]]==1 )
{
printf("%02d:%02d ",t[i].l/60,t[i].l%60);
printf("%02d:%02d\n",(t[i].l+t[i].c)/60,(t[i].l+t[i].c)%60);
}
else
{
printf("%02d:%02d ",(t[i].r-t[i].c)/60,(t[i].r-t[i].c)%60);
printf("%02d:%02d\n",t[i].r/60,t[i].r%60);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a,b,c,d;
scanf("%d:%d%d:%d%d",&a,&b,&c,&d,&t[i].c);
t[i].l=a*60+b,t[i].r=c*60+d;
}
build();
if(!solve()) printf("NO\n");
else
{
printf("YES\n");
rebuild();
topsort();
print();
}
return 0;
}