题意:有若干对新人结婚,每个婚礼都会一个特殊仪式。对于每个婚礼有三项输入:开始时间(s)、结束时间(t)和特殊仪式的持续时间(last)。已知特殊仪式只能在婚礼的开始阶段或者结束阶段举行(即在s~s+last或者t-last~t)。特殊仪式必须要有牧师才行,而该村只有一个牧师,问合理安排各个婚礼的特殊仪式时间能否使得牧师参加所有的婚礼,如果可以,输出特殊仪式举行时间的一种方案?
思路:显然为2-SAT,难点在输出方案。
需要输出方案的2-SAT总结如下:
1、构图。
2、tarjan求极大强连通子图。
3、判断是否有解,无解则直接输出(判断的时候记录冲突,conflict数组,这里还不是特别明白!!!)
4、把每个强连通子图收缩成单个节点,根据原图关系反向构造一个有向无环图
5、对新图进行拓扑排序,并进行染色(color数组)
6、记录哪些点赋值为“0”,哪些赋值为“1”
7、输出:
#include <stdio.h> #include <string.h> #define cle(a) memset(a,0,sizeof(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define N 1005*2 struct node{//保存时间,x和y为起始和结束的时间(以分钟计) int x,y,last; }p[N]; struct edge{ int y,next; }e[N*N*2],e_t[N*N*2]; int first[N],first_t[N],dfn[N],low[N],stack[N],strong[N],d[N]; int conflict[N],color[N],res[N],q[N]; int n,id,top,tops,top_t,num; void init(){ tops = -1; id = top = top_t = num = 0; memset(first,-1,sizeof(first)); memset(first_t,-1,sizeof(first_t)); memset(dfn,-1,sizeof(dfn)); cle(strong); cle(d); cle(color); cle(res); } void add(int x,int y){ e[top].y = y; e[top].next = first[x]; first[x] = top++; } void add2(int x,int y){//缩点后建立的新图,为拓扑排序 e_t[top_t].y = y; e_t[top_t].next = first_t[x]; first_t[x] = top_t++; } int test_cross(int i,int j,int a,int b){//判断两个时间段是否冲突,0表示开始阶段,1表示结束阶段 int x1,y1,x2,y2; if(!a){ x1 = p[i].x; y1 = p[i].x + p[i].last; }else{ x1 = p[i].y - p[i].last; y1 = p[i].y; } if(!b){ x2 = p[j].x; y2 = p[j].x + p[j].last; }else{ x2 = p[j].y - p[j].last; y2 = p[j].y; } return (x1<y2) && (x2<y1); } void tarjan(int x){ int i,y; dfn[x] = low[x] = ++id; stack[++tops] = x; for(i = first[x];i!=-1;i=e[i].next){ y = e[i].y; if(dfn[y] == -1){ tarjan(y); low[x] = min(low[x],low[y]); }else if(!strong[y]) low[x] = min(low[x],dfn[y]); } if(dfn[x] == low[x]){ num++; do{ strong[stack[tops]] = num; }while(stack[tops--]!=x); } } void create_graph(){//建立新图 int i,j; for(i = 1;i<=n<<1;i++) for(j = first[i];j!=-1;j=e[j].next) if(strong[i] != strong[e[j].y]){ d[strong[i]]++; add2(strong[e[j].y],strong[i]); } } void topsort(){//求拓扑排序,并染色 int i,now,front,rear; front = rear = -1; for(i = 1;i<=num;i++) if(!d[i]) q[++rear] = i; while(front < rear){ now = q[++front]; if(!color[now]){ color[now] = 1; color[conflict[now]] = -1; } for(i = first_t[now];i!=-1;i=e_t[i].next){ --d[e_t[i].y]; if(!d[e_t[i].y]) q[++rear] = e_t[i].y; } } } int main(){ freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF){ int i,j,h1,h2,m1,m2; init(); for(i = 1;i<=n;i++){//读入,并将时间转化为分钟 scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&p[i].last); p[i].x = h1*60+m1; p[i].y = h2*60+m2; } for(i = 1;i<n;i++)//分别判断是否冲突,建图tarjan for(j = i+1;j<=n;j++){ if(test_cross(i,j,0,0)){ add(i,j+n);add(j,i+n); } if(test_cross(i,j,0,1)){ add(i,j);add(j+n,i+n); } if(test_cross(i,j,1,0)){ add(i+n,j+n);add(j,i); } if(test_cross(i,j,1,1)){ add(i+n,j);add(j+n,i); } } for(i = 1;i<=n<<1;i++)//tarjan if(dfn[i] == -1) tarjan(i); for(i = 1;i<=n;i++){ if(strong[i] == strong[i+n]) break; conflict[strong[i]] = strong[i+n];//记录冲突???? conflict[strong[i+n]] = strong[i]; } if(i>n){ printf("YES\n"); create_graph(); topsort(); for(i = 1;i<=n;i++) if(color[strong[i]] == 1)//所在强连通分量染色为1的点表示取到 res[i] = 1; for(i = 1;i<=n;i++) if(res[i] == 1){//表示婚礼开始阶段 printf("%02d:%02d ",p[i].x/60,p[i].x%60); printf("%02d:%02d\n",(p[i].x+p[i].last)/60,(p[i].x+p[i].last)%60); }else{//婚礼结束阶段 printf("%02d:%02d ",(p[i].y-p[i].last)/60,(p[i].y-p[i].last)%60); printf("%02d:%02d\n",p[i].y/60,p[i].y%60); } }else printf("NO\n"); } return 0; }