POJ 3683 Priest Phon's Busiest Day

蟻本復習中。

問題

N組のカップルが同じ日に結婚式を挙げる。
i番目の結婚式は時刻s[i]からt[i]の間に開かれ、その最初か最後のどちらかのd[i]分間特別な式典を行う必要がある。
(s[i]〜s[i]+d[i]かt[i]-d[i]〜t[i]のどちらか)


式典には司祭の出席が必要で、司祭は同時に二つ以上の式典に出ることはできない。
ただし、司祭の移動時間は無視でき、終了時間と開始時間の等しい2つの式典に出席することもできる。


司祭が全ての式典に出席できるかを判定せよ。
可能なら、どの時間帯の式典に出席するか、可能な答え一つを出力せよ。

制約条件

N≦1000

方針

蟻本の解説の通り。
結婚式iの、前のほうの式典に出席することをxi、後ろに出席することを¬xiとする。
式典i,jがバッティングしている場合、¬(xi∧xj)が成り立つ必要があるが、これは= (¬xi∨¬xj)と変形できるため、2-SAT問題に帰着できる。

ソースコード

蟻本の実装はもう少し綺麗に書ける。
こんな感じ。

int n,s[2000],d[2000];

int main(){
	int n; scanf("%d",&n);
	rep(i,n){
		int a,b,x,y;
		scanf("%d:%d %d:%d %d",&a,&b,&x,&y,d+i);
		s[i]=a*60+b;
		s[i+n]=x*60+y-d[i];
		d[i+n]=d[i];
	}
	V=2*n;
	rep(i,2*n)rep(j,i){
		if(!(s[j]+d[j]<=s[i]||s[i]+d[i]<=s[j])){
			#define NOT(x) (x<n?x+n:x-n)
			add_edge(i,NOT(j));
			add_edge(j,NOT(i));
		}
	}
	scc();
	
	rep(i,n){
		if(cmp[i]==cmp[i+n]){
			printf("NO\n");
			return 0;
		}
	}
	
	printf("YES\n");
	rep(i,n){
		if(cmp[i]>cmp[i+n])printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+d[i])/60,(s[i]+d[i])%60);
		else printf("%02d:%02d %02d:%02d\n",s[i+n]/60,s[i+n]%60,(s[i+n]+d[i])/60,(s[i+n]+d[i])%60);
	}
	return 0;
}