题意:有若干对新人结婚,每个婚礼都会一个特殊仪式。对于每个婚礼有三项输入:开始时间(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;
}