POJ 3683 Priest John's Busiest Day

题意: John是一个牧师,他们家乡人喜欢挤在一天结婚,而且都要John来主持。每段婚礼都必须有一个仪式,要么在婚礼开始的时候开始,要么正好在婚礼结束的时候结束。告诉你每段婚礼的开始时间和结束时间以及仪式持续时间。问:John能不能正好主持完所以仪式,没有时间冲突,输出每个婚礼无冲突的仪式始末时间。


思路: 2-sat模版题。设婚礼开始时启动仪式为A,反之为A'。如果A与B有冲突,那么连接A——B',以此类推这样建图。然后求强连通分量,判断是否有解,有解的话缩图,然后Topsort一下输出。


代码:

#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
using namespace std;

#define N 1010

int n;

struct couple{
	int s,t,d;
	void init(char begin[],char end[],int dur)
	{
		int h,m;
		d=dur;
		sscanf(begin,"%d:%d",&h,&m);
		s=h*60+m;
		sscanf(end,"%d:%d",&h,&m);
		t=h*60+m;
	}
}c[N];

struct edge{
	int id,next;
};

class graph{
private:
	int eid,p[N<<1];
	edge e[(N*N)<<3];
public:
	void clear()
	{
		eid=0;
		memset(p,-1,sizeof(p));
	}
	void insert(int from,int to)
	{
		e[eid].id=to;
		e[eid].next=p[from];
		p[from]=eid++;
	}
	int next(int i){return e[i].next;}
	int head(int u){return p[u];}
	int getid(int i){return e[i].id;}
}G,newG;

class twoSat{
private:
	int top,index,newid,stack[N<<1],
		instack[N<<1],in[N<<1],other[N<<1],
		belong[N<<1],dfn[N<<1],low[N<<1],
		color[N<<1];
public:
	void clear()
	{
		top=index=newid=0;
		memset(stack,0,sizeof(stack));
		memset(instack,0,sizeof(instack));
		memset(belong,0,sizeof(belong));
		memset(dfn,0,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(other,0,sizeof(other));
		memset(in,0,sizeof(in));
		memset(color,0,sizeof(color));
	}
	bool isvisit(int v){return dfn[v];}
	bool legal(int x1,int x2,int y1,int y2)
	{
		return x2<=y1 || y2<=x1;
	}
	void makeMap()
	{
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				if(!legal(c[i].s , c[i].s+c[i].d , c[j].s , c[j].s+c[j].d))
					G.insert(i,j+n);
				if(!legal(c[i].s , c[i].s+c[i].d , c[j].t-c[j].d , c[j].t))
					G.insert(i,j);
				if(!legal(c[i].t-c[i].d , c[i].t , c[j].s , c[j].s+c[j].d))
					G.insert(i+n,j+n);
				if(!legal(c[i].t-c[i].d , c[i].t , c[j].t-c[j].d , c[j].t))
					G.insert(i+n,j);
			}
		}
	}
	void Tarjan(int u)
	{
		int v,t;
		dfn[u]=low[u]=++index;
		stack[++top]=u;
		instack[u]=1;
		for(int i=G.head(u);i!=-1;i=G.next(i)){
			v=G.getid(i);
			if(!dfn[v]){
				Tarjan(v);
				low[u]=min(low[u],low[v]);
			}else if(instack[v]){
				low[u]=min(low[u],dfn[v]);
			}
		}
		if(dfn[u]==low[u]){
			newid++;
			do{
				t=stack[top--];
				instack[t]=0;
				belong[t]=newid;
			}while(t!=u);
		}
	}
	void contract()
	{
		int i,u,v;
		newG.clear();
		for(u=1;u<=2*n;u++){
			for(i=G.head(u);i!=-1;i=G.next(i)){
				v=G.getid(i);
				if(belong[u]!=belong[v]){
					newG.insert(belong[v],belong[u]);
					in[belong[u]]++;
				}
			}
		}
	}
	bool exist()
	{
		for(int i=1;i<=n;i++){
			if(belong[i]==belong[i+n]) return false ; //判断是否有解
			other[belong[i]]=belong[i+n];
			other[belong[i+n]]=belong[i];
		}
		return true;
	}
	void Topsort()
	{
		int i,u,v;
		queue<int> q;
		for(i=1;i<=newid;i++)
			if(!in[i]) q.push(i);
		while(!q.empty()){
			u=q.front(),q.pop();
			if(!color[u]){
				color[u]=1;
				color[other[u]]=-1;
			}
			for(i=newG.head(u);i!=-1;i=newG.next(i)){
				v=newG.getid(i);
				if( !(--in[v]) ) q.push(v);
			}
		}
	}
	void putAnswer()
	{
		int h,m;
		Topsort();
		for(int i=1;i<=n;i++){
			if(color[belong[i]]==1){
				h=c[i].s/60;
				m=c[i].s%60;
				printf("%02d:%02d ",h,m);
				h=(c[i].s+c[i].d)/60;
				m=(c[i].s+c[i].d)%60;
				printf("%02d:%02d\n",h,m);
			}else{
				h=(c[i].t-c[i].d)/60;
				m=(c[i].t-c[i].d)%60;
				printf("%02d:%02d ",h,m);
				h=c[i].t/60;
				m=c[i].t%60;
				printf("%02d:%02d\n",h,m);
			}
		}
	}
}TS;

void init()
{
	G.clear();
	TS.clear();
}

void solve()
{
	int i;
	TS.makeMap();                //建图
	for(i=1;i<=2*n;i++)        //求强连通分量
		if(!TS.isvisit(i))
			TS.Tarjan(i);
	if(TS.exist()){                   //判断是否有解
		TS.contract();         //缩图      
		puts("YES");
		TS.putAnswer();    //输出答案
	}else puts("NO");
}

int main()
{
	int duration;
	char begin[20],end[20];
	while(~scanf("%d",&n)){
		init();
		for(int i=1;i<=n;i++){
			scanf("%s %s %d",begin,end,&duration);
			c[i].init(begin,end,duration);
		}
		solve();
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值