http://poj.org/problem?id=3683
题意:有N场婚礼,每场婚礼有一个开始时间和结束时间,并且每场婚礼都会举行一个仪式,这个仪式需要有一个司仪,并且这个仪式要么就在婚礼的一开始就举行,要么就在婚礼快结束的时候举行。但是现在只有一个司仪,司仪不能在同一时间参加两个不同婚礼的仪式,问是否存在这样的安排,使得司仪参见这些仪式不冲突,若不存在则输出“NO” , 若存在,则输出一种安排。
思路:2-Sat问题,这是一个典型的2-Sat问题,建图的顶点是每次婚礼的仪式的时间,对于第i场婚礼,两个时间分别用点i和i+N表示,冲突的情况分析如下,假设i举行仪式的时间是:[ a ,b ] , j举行仪式的时间是:[ c, d ] ,则两者不冲突的条件是:b <= c || d <= a (边界冲突不算冲突)。建图之后强两通缩点,缩点完了之后进行判断,判断是否有同一场婚礼的不同的仪式时间在同一个强连通分量里面,最后求一次拓扑排序求出任意一种可行的方案。
代码:
#include<stdio.h>
#include<string.h>
const int MAXN = 2010 ;
int N ;
int s[MAXN] , e[MAXN] ;
int Gnext[MAXN*MAXN],Gv[MAXN*MAXN],Gr[MAXN],Gc ;
int dfn[MAXN] , low[MAXN] , stack[MAXN] , belong[MAXN] ,cf[MAXN];
bool in[MAXN] ;
int top ,idx ,Bcnt ;
int gnext[MAXN*MAXN],gv[MAXN*MAXN],gr[MAXN],gc ;
int degree[MAXN],que[MAXN] ,front ,rear ,col[MAXN];
bool is_inter(int i , int j){
int a ,b,c,d;
a = s[i] ; b = e[i] ; c=s[j] ; d=e[j] ;
if( b<=c || d<=a ) return false ;
return true ;
}
void add(int a, int b){
Gv[Gc] = b ;
Gnext[Gc] = Gr[a] ;
Gr[a] = Gc++ ;
}
void build(){
memset(Gr,-1,sizeof(Gr)) ; Gc = 0 ;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
if(i == j) continue ;
if( is_inter(i,j) ) add(i,j+N); //建图
if( is_inter(i,j+N) ) add(i,j) ;
if( is_inter(i+N,j) ) add(i+N,j+N);
if( is_inter(i+N,j+N) ) add(i+N,j) ;
}
}
}
void tarjin(int u){
int v ;
low[u] = dfn[u] = ++idx ;
stack[++top] = u ; in[u] = 1 ;
for(int i=Gr[u] ;i!=-1;i=Gnext[i]){
v = Gv[i] ;
if( !dfn[v] ){
tarjin(v) ; if(low[v] < low[u]) low[u] = low[v] ;
}
else if( in[v] && dfn[v]<low[u])
low[u] = dfn[v] ;
}
if( low[u] == dfn[u] ){
Bcnt++ ;
do{
v = stack[top--] ; in[v] = 0 ;
belong[v] = Bcnt ;
}while(v != u) ;
}
}
void add2(int a, int b){
gv[gc] = b ;
gnext[gc] = gr[a] ;
gr[a] = gc++ ;
}
void solve(){
top = idx = Bcnt = 0 ;
memset(dfn , 0 ,sizeof(dfn)) ;
memset(in , 0 ,sizeof(in) ) ;
for(int i=1;i<=2*N;i++){
if( !dfn[i] ) tarjin(i);
}
bool ok = 1 ;
for(int i=1;i<=N;i++){
if( belong[i] == belong[i+N] ){
ok = 0 ; break ;
}
/*
为下面的拓扑排序作准备。
*/
cf[ belong[i] ] = belong[i+N] ;
cf[ belong[i+N] ] = belong[i] ;
}
if( !ok ){
printf("NO\n"); return ;
}
printf("YES\n");
memset( gr,-1,sizeof(gr)); gc = 0 ;
memset(degree, 0 ,sizeof(degree));
for(int i=1;i<=2*N;i++){ //缩点,建新图
for(int j=Gr[i] ;j!=-1;j=Gnext[j]){
int v = Gv[j] ;
int a = belong[i] ;
int b = belong[v] ;
if(a != b){
add2(b,a);
degree[a] ++ ;
}
}
}
front = rear = 0 ;
for(int i=1;i<=Bcnt;i++){
if( degree[i] == 0 ) que[rear++] = i ;
}
memset(col, 0,sizeof(col)) ;
while(front != rear){
int u = que[front++] ;
if( col[u] == 0 ){
col[u] = 1 ;
col[ cf[u] ] = -1 ;
}
for(int i=gr[u];i!=-1;i=gnext[i]){
int v = gv[i] ;
if( --degree[v] == 0 ) que[rear++] =v ;
}
}
int a, b, c ,d ;
for(int i=1;i<=N;i++){
if( col[ belong[i] ] == 1){
a = s[i] / 60 ;
b = s[i] % 60 ;
c = e[i] / 60 ;
d = e[i] % 60 ;
printf("%02d:%02d %02d:%02d\n",a,b,c,d);
}
else{
a = s[i+N] / 60 ;
b = s[i+N] % 60 ;
c = e[i+N] / 60 ;
d = e[i+N] % 60 ;
printf("%02d:%02d %02d:%02d\n",a,b,c,d);
}
}
}
int main(){
int a,b, c ,d ,ee;
while(scanf("%d",&N) == 1){
for(int i=1;i<=N;i++){
scanf("%d:%d %d:%d %d",&a,&b,&c,&d,&ee);
a = a*60+b ;
c = c*60+d ;
s[i]=a ; e[i]=a+ee ;
s[i+N]=c-ee ; e[i+N]=c ;
}
build() ;
solve() ;
}
return 0 ;
}