POJ3683:Priest John‘s Busiest Day(2-SAT)

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);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值