Priest John's Busiest Day
Description John is the only priest in his town. September 1st is the John's busiest day in a year because there is an old legend in the town that the couple who get married on that day will be forever blessed by the God of Love. This year N couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from time Si to time Ti. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. The i-th couple need Di minutes to finish this ceremony. Moreover, this ceremony must be either at the beginning or the ending of the wedding (i.e. it must be either from Si to Si + Di, or from Ti - Di to Ti). Could you tell John how to arrange his schedule so that he can present at every special ceremonies of the weddings. Note that John can not be present at two weddings simultaneously. Input The first line contains a integer N ( 1 ≤ N ≤ 1000). Output The first line of output contains "YES" or "NO" indicating whether John can be present at every special ceremony. If it is "YES", output another N lines describing the staring time and finishing time of all the ceremonies. Sample Input 2 08:00 09:00 30 08:15 09:00 20 Sample Output YES 08:00 08:30 08:40 09:00 Source
POJ Founder Monthly Contest – 2008.08.31, Dagger and Facer
|
题意:一个小镇里面只有一个牧师,现在有些新人要结婚,需要牧师分别去主持一个仪式,给出每对新人婚礼的开始时间 s 和结束时间 t ,还有他们俩的这个仪式需要的时间(每对新人需要的时间长短可能不同) d ,牧师可以在婚礼开始的时间 d 内(s 到 s+d)或者是结束前的时间 d 内(t - d 到 t)完成这个仪式。现在问能否给出一种安排,让牧师能完成所有夫妇婚礼的仪式,如果可以,输出一种安排。
我们把每个婚礼可能的两个时间段看做两个点 A 和 A’,显然,如果两个时间段冲突(比如 A 和 B 的时间重合),那么需要建边(A -> B' ),(B -> A‘),判断出是否存在解后,需要输出一组解,这里的方法是 赵爽 的《2-SAT 解法浅析》里面看的,有详细的证明。
具体操作就是:求强联通,缩点重新建图(这里建反向图,参见论文),然后给图中的点着色,将一个未着色点 x 上色同时,把与它矛盾的点 y 以及 y 的所有子孙节点上另外一种颜色,上色完成后,进行拓扑排序,选择一种颜色的点输出就是一组可行解。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int VM=4010; const int EM=2000010; struct Point{ int st,et; }p[VM]; struct Edge{ int frm,to,nxt; }edge1[EM<<1],edge2[EM<<1]; int n,m,tot,cnt1,cnt2,dep,top,atype,head1[VM],head2[VM]; int dfn[VM],low[VM],vis[VM],belong[VM],indeg[VM]; int stack[VM],ans[VM],mark[VM],color[VM],que[VM]; //color[]为是否选择标志 //1表示选择,0表示不选择 void Init(){ cnt1=0, cnt2=0, atype=0, dep=0, top=0, tot=0; memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); memset(vis,0,sizeof(vis)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(belong,0,sizeof(belong)); memset(indeg,0,sizeof(indeg)); memset(ans,0,sizeof(ans)); memset(mark,0,sizeof(mark)); memset(color,0,sizeof(color)); } void addedge1(int cu,int cv){ //原图增加一条边 edge1[cnt1].frm=cu; edge1[cnt1].to=cv; edge1[cnt1].nxt=head1[cu]; head1[cu]=cnt1++; } void addedge2(int cu,int cv){ //缩点图增加一条边 edge2[cnt2].frm=cu; edge2[cnt2].to=cv; edge2[cnt2].nxt=head2[cu]; head2[cu]=cnt2++; } int judge(int a,int b){ if( p[a].et <= p[b].st || p[b].et <= p[a].st )return 1; return 0; } void Tarjan(int u){ //Tarjan算法求强连通分量 dfn[u]=low[u]=++dep; stack[top++]=u; vis[u]=1; for(int i=head1[u];i!=-1;i=edge1[i].nxt){ int v=edge1[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(vis[v]) low[u]=min(low[u],dfn[v]); } int j; if(dfn[u]==low[u]){ atype++; do{ j=stack[--top]; belong[j]=atype; vis[j]=0; }while(u!=j); } } void solve(){ for(int i=0;i<n;i++) //n表示点的个数 if(!dfn[i]) Tarjan(i); int flag=1; for(int i=0;i<m;i++){ //共 m 场婚礼,注意这里的m=n/2 if(belong[2*i]==belong[2*i+1]){ flag=0; break; } //这里,为着色做准备,mark[x]保存的是和编号为 x 的连通分量矛盾的连通分量的编号 //在赵爽的论文中有证明,这样可以保证拓扑求解的可行性 //b 和 b' 所在的连通分量是矛盾的(如果不矛盾,那么2-SAT无解) mark[ belong[2*i] ]=belong[2*i+1]; mark[ belong[2*i+1] ]=belong[2*i]; } if(flag==0){ printf("NO\n"); return ; } printf("YES\n"); for(int i=0;i<cnt1;i++) if(belong[edge1[i].frm]!=belong[edge1[i].to]){ addedge2(belong[edge1[i].to],belong[edge1[i].frm]); indeg[belong[edge1[i].frm]]++; } int head=1,tail=1; //拓扑排序求解 for(int i=1;i<=atype;i++) if(indeg[i]==0) //入度为0入队列 que[tail++]=i; while(head<tail){ int u=que[head++]; if(color[u]==0){ //对于未着色的点x,将x染成红色1,同时将与x矛盾的点cf[x]染成蓝色-1。 color[u]=1; color[mark[u]]=-1; } for(int i=head2[u];i!=-1;i=edge2[i].nxt){ int v=edge2[i].to; if(--indeg[v]==0) //入度为0 que[tail++]=v; //入队列 } } int a,b,c,d; for(int i=0;i<m;i++){ if(color[ belong[2*i] ]==1){//如果颜色和所选颜色相同,输出点 b a=p[2*i].st/60; b=p[2*i].st%60; c=p[2*i].et/60; d=p[2*i].et%60; printf("%02d:%02d %02d:%02d\n",a,b,c,d); } else{//否则输出点 b' a=p[2*i+1].st/60; b=p[2*i+1].st%60; c=p[2*i+1].et/60; d=p[2*i+1].et%60; printf("%02d:%02d %02d:%02d\n",a,b,c,d); } } } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d",&m)){ Init(); int a,b,c,d,t; for(int i=0;i<m;i++){ scanf("%d:%d %d:%d %d",&a,&b,&c,&d,&t); int t1=60*a+b, t2=60*c+d; p[tot].st=t1, p[tot++].et=t1+t; p[tot].st=t2-t, p[tot++].et=t2; } for(int i=0;i<m;i++) for(int j=0;j<m;j++) if(i!=j){ if(judge(2*i,2*j)==0) addedge1(2*i,2*j+1); if(judge(2*i,2*j+1)==0) addedge1(2*i,2*j); if(judge(2*i+1,2*j)==0) addedge1(2*i+1,2*j+1); if(judge(2*i+1,2*j+1)==0) addedge1(2*i+1,2*j); } n=tot; //n表示点的个数 solve(); } return 0; }