POJ3683:Priest John’s Busiest Day(2-SAT)
题意:
某一天结婚的人特别多但是主持婚礼的神父只有一个。婚礼时间从s开始到e结束,神父必须在s到s+d或者e-d到e这段时间内在。给定了n个婚礼的s,e,d,求一种方案能使得神父主持所有的婚礼。
思路:
共n个婚礼,每个婚礼有两种选择。因为有相互影响,所以可以枚举两次婚礼的两次时间进行建边。输出方案时,因为tarjan求的是逆拓扑序,编号小的拓扑序大。我们需要选择对于每次婚礼的两种情况中拓扑序小的。
#include<iostream>
#include<stack>
using namespace std;
typedef long long ll;
const int maxn=3e6+5;
const ll inf=2e9+5;
int n,m,k,cnt,num,vis[maxn],dfn[maxn],low[maxn],op[maxn],ans[maxn],id[maxn];
int head[maxn],nex[maxn],to[maxn];
stack<int>pd;
struct node{
int s1,t1,s2,t2;
}p[maxn];
void add(int a,int b){
to[++k]=b;
nex[k]=head[a];
head[a]=k;
}
bool check(int s1,int t1 ,int s2,int t2){
if(s1>=t2||t1<=s2)return 0;
return 1;
}
void tarjan(int x){ //有向边
low[x]=dfn[x]=++num;
pd.push(x);
vis[x]=1;
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
cnt++;//重新编号
int now;
do{
now=pd.top();
pd.pop();
vis[now]=0;
id[now]=cnt;//记录每个点编号
}while(now!=x);
}
}
int main(){
scanf("%d",&n);
for(int i=1,t1,t2,t3,t4,t;i<=n;i++){
scanf("%d:%d %d:%d %d",&t1,&t2,&t3,&t4,&t);
p[i].s1=t1*60+t2;
p[i].t1=t1*60+t2+t;
p[i].s2=t3*60+t4-t;
p[i].t2=t3*60+t4;
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(check(p[i].s1,p[i].t1,p[j].s1,p[j].t1))add(i,j+n),add(j,i+n);//前前
if(check(p[i].s1,p[i].t1,p[j].s2,p[j].t2))add(i,j),add(j+n,i+n);//前后
if(check(p[i].s2,p[i].t2,p[j].s1,p[j].t1))add(i+n,j+n),add(j,i);
if(check(p[i].s2,p[i].t2,p[j].s2,p[j].t2))add(i+n,j),add(j+n,i);
}
}
for(int i=1;i<=2*n;i++)
if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++){
if(id[i]==id[i+n]){
printf("NO\n");
return 0;
}
// op[i]=n+i,op[n+i]=i;
}
printf("YES\n");
/*for(int i=1;i<=2*n;i++){
ans[i]=id[i]>id[op[i]];
}*/
for(int i=1;i<=n;i++){
if(id[i]<id[i+n])printf("%02d:%02d %02d:%02d\n",p[i].s1/60,p[i].s1%60,p[i].t1/60,p[i].t1%60);
else printf("%02d:%02d %02d:%02d\n",p[i].s2/60,p[i].s2%60,p[i].t2/60,p[i].t2%60);
}
}