POJ 3683 Priest John's Busiest Day【2-sat】
题意:有 n 对新人结婚,只有一个牧师,知道了每个婚礼的开始时间 s,和结束时间 t 和需要牧师的主持时间 las,牧师可以选在在[s,s+las]或[t-las,t]两个时间段内主持,
问是否存在一个时间安排,使得所有新人都可以得到牧师的主持。
分析: 每一个婚礼主持时间都是两种状态,每个婚礼之间的时间可能会互相限制,可以用2-sat判断是否冲突。
建图:
每个婚礼的两个时间段 i = [s,s+las],i +n = [t-las,t]
如果 i 和 j 冲突,建边 i -> j + n
如果 i 和 j + n 冲突,建边 i -> j
如果 i + n 和 j 冲突,建边 i + n -> j + n
如果 i + n 和 j + n 冲突,建边 i + n -> j
求出强连通分量并染色,判断是否有 i 和 i+n在一个集合的情况,如果有则不存在
否则反向拓扑排序,找出一组解即可。
#include<stdio.h> #include<string.h> #define min(a,b)(a)<(b)?(a):(b) #define max(a,b)(a)>(b)?(a):(b) #define clr(x)memset(x,0,sizeof(x)) #define maxn 2100 #define maxm 3000000 struct node { int from,to,next; }e[maxm],sed[maxm]; int head[maxn]; int sorh[maxn]; int tot; int tt; void add(int s,int t) { e[tot].from=s; e[tot].to=t; e[tot].next=head[s]; head[s]=tot++; } void add2(int s,int t) { sed[tt].to=t; sed[tt].next=sorh[s]; sorh[s]=tt++; } int ti,sn,top,n; int low[maxn]; int dfn[maxn]; int ins[maxn]; int sta[maxn]; int col[maxn]; int sco[maxn]; int ct[maxn]; int q[maxn]; int ind[maxn]; int res[maxn]; void tarjan(int u) { dfn[u]=low[u]=++ti; ins[u]=1; sta[++top]=u; int i,k; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(dfn[k]==0) { tarjan(k); low[u]=min(low[u],low[k]); } else if(ins[k]) low[u]=min(low[u],dfn[k]); } if(dfn[u]==low[u]) { sn++; do { k=sta[top--]; ins[k]=0; sco[k]=sn; }while(k!=u); } } struct edge { int s1,e1,s2,e2; }p[maxn]; char st[22],en[22]; int main() { scanf("%d",&n); int i,j,k,las,front,rear; for(i=0;i<n;i++) { scanf("%s%s%d",st,en,&las); p[i].s1=((st[0]-'0')*10+st[1]-'0')*60+(st[3]-'0')*10+st[4]-'0'; p[i].e1=p[i].s1+las; p[i].e2=((en[0]-'0')*10+en[1]-'0')*60+(en[3]-'0')*10+en[4]-'0'; p[i].s2=p[i].e2-las; } clr(head); tot=1; for(i=0;i<n;i++) for(j=0;j<n;j++) { if(i==j) continue; if(p[i].s1<p[j].e1&&p[j].s1<p[i].e1) add(i,j+n); if(p[i].s1<p[j].e2&&p[j].s2<p[i].e1) add(i,j); if(p[i].s2<p[j].e1&&p[j].s1<p[i].e2) add(i+n,j+n); if(p[i].s2<p[j].e2&&p[j].s2<p[i].e2) add(i+n,j); } ti=sn=top=0; clr(sco); clr(dfn); clr(low); clr(ins); for(i=0;i<2*n;i++) if(!dfn[i]) tarjan(i); int flag=0; for(i=0;i<n;i++) { if(sco[i]==sco[i+n]) flag=1; ct[sco[i]]=sco[i+n]; ct[sco[i+n]]=sco[i]; } if(flag) goto loop; tt=1; clr(sorh); clr(ind); clr(col); for(i=1;i<tot;i++) if(sco[e[i].from]!=sco[e[i].to]) { add2(sco[e[i].to],sco[e[i].from]); ind[sco[e[i].from]]++; } front=0,rear=0; for(i=1;i<=sn;i++) if(ind[i]==0) q[rear++]=i; while(front<rear) { int x=q[front++]; if(col[x]==0) { col[x]=1; col[ct[x]]=-1; } for(i=sorh[x];i;i=sed[i].next) { k=sed[i].to; if(--ind[k]==0) q[rear++]=k; } } clr(res); for(i=0;i<2*n;i++) if(col[sco[i]]==1) res[i]=1; loop: if(flag) printf("NO\n"); else { printf("YES\n"); for(i=0;i<n;i++) { if(res[i]) printf("%02d:%02d %02d:%02d\n",p[i].s1/60,p[i].s1%60,p[i].e1/60,p[i].e1%60); else printf("%02d:%02d %02d:%02d\n",p[i].s2/60,p[i].s2%60,p[i].e2/60,p[i].e2%60); } } return 0; }