POJ 3683 Priest John's Busiest Day(2-SAT输出方案)

解决一场忙碌的神父如何在多场婚礼中,合理安排特殊仪式时间,确保每场婚礼的仪式都能完整参与的问题。通过建立二部图并使用二分图匹配算法求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Priest John's Busiest Day

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 12491 Accepted: 4252 Special Judge

Description

John is the only priest in his town. September 1st is the John's busiest day in a year because there is an old legend in the town that the couple who get married on that day will be forever blessed by the God of Love. This year N couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from time Si to time Ti. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. The i-th couple need Di minutes to finish this ceremony. Moreover, this ceremony must be either at the beginning or the ending of the wedding (i.e. it must be either from Si to Si + Di, or from Ti - Di to Ti). Could you tell John how to arrange his schedule so that he can present at every special ceremonies of the weddings.

Note that John can not be present at two weddings simultaneously.

Input

The first line contains a integer N ( 1 ≤ N ≤ 1000). 
The next N lines contain the SiTi and DiSi and Ti are in the format of hh:mm.

Output

The first line of output contains "YES" or "NO" indicating whether John can be present at every special ceremony. If it is "YES", output another N lines describing the staring time and finishing time of all the ceremonies.

Sample Input

2
08:00 09:00 30
08:15 09:00 20

Sample Output

YES
08:00 08:30
08:40 09:00

Source

POJ Founder Monthly Contest – 2008.08.31, Dagger and Facer

题意:

n场婚礼,每场婚礼的开始时间为s,结束时间为t,每场婚礼有一个时间为d的仪式,这个仪式要么在开始的时刻开始,要么在最后开始(t-d开始),问能否安排每场婚礼举行仪式的时间,使主持人能参加所有的这些仪式的全过程。

分析:

我们设Xi可以第i场婚礼参加开头为1,不在开头(即参加结尾)为0

加边:参考:https://blog.csdn.net/V5ZSQ/article/details/47168479

如果Si~Si+Di 与 Sj~Sj+Dj冲突,则Xi->~Xj,Xj->~Xi 
如果Si~Si+Di 与 Tj -Dj~Tj冲突,则Xi->Xj,~Xj->~Xi 
如果Ti -Di~Ti 与 Sj~Sj+Dj冲突,则Xj->Xi,~Xi->~Xj 
如果Ti -Di~Ti 与 Tj -Dj~Tj冲突,则~Xi->Xj,~Xj->Xi 
 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
const int maxn=10000+10;
struct node
{
    int s,e,d;
    node(){}
    node(int s,int e,int d):s(s),e(e),d(d){}
}t[maxn];
int judge(int st1,int et1,int st2,int et2){
	if( et1 <= st2 ||et2 <= st1 )return 1;
	return 0;
}
struct TwoSAT
{
    int n;//原始图的节点数(未翻倍)
    vector<int> G[maxn*2];//G[i]==j表示如果mark[i]=true,那么mark[j]也要=true
    bool mark[maxn*2];//标记
    int S[maxn*2],c;//S和c用来记录一次dfs遍历的所有节点编号

    void init(int n)
    {
        this->n=n;
        for(int i=0;i<2*n;i++) G[i].clear();
        memset(mark,0,sizeof(mark));
    }

    //加入(x,xval)或(y,yval)条件
    //xval=0表示假,yval=1表示真
    void add_clause(int x,int xval,int y,int yval)
    {
        x=x*2+xval;
        y=y*2+yval;
        G[x].push_back(y);
       // G[y^1].push_back(x);
    }

    //从x执行dfs遍历,途径的所有点都标记
    //如果不能标记,那么返回false
    bool dfs(int x)
    {
        if(mark[x^1]) return false;//这两句的位置不能调换
        if(mark[x]) return true;
        mark[x]=true;
        S[c++]=x;
        for(int i=0;i<G[x].size();i++)
            if(!dfs(G[x][i])) return false;
        return true;
    }

    //判断当前2-SAT问题是否有解
    bool solve()
    {
        for(int i=0;i<2*n;i+=2)
        if(!mark[i] && !mark[i+1])
        {
            c=0;
            if(!dfs(i))
            {
                while(c>0) mark[S[--c]]=false;
                if(!dfs(i+1)) return false;
            }
        }
        return true;
    }
}TS;
int main()
{
	 int n,m;
	 int a,b,c,d;
	 char op[10];
	 while(scanf("%d",&n)!=-1)
	{
		TS.init(n);
		for(int i=0;i<n;i++)
		{
		  int sh,sm,eh,em,d;
          scanf("%d:%d %d:%d %d",&sh,&sm,&eh,&em,&d);
          t[i]=node(sh*60+sm,eh*60+em,d);
		}
		for(int i=0;i<n;i++)
      		for(int j=i+1;j<n;j++)
    	{
			if(judge(t[i].s,t[i].s+t[i].d,t[j].s,t[j].s+t[j].d)==0)
			{
				TS.add_clause(i,0,j,1);
				TS.add_clause(j,0,i,1);
			}
			if(judge(t[i].s,t[i].s+t[i].d,t[j].e-t[j].d,t[j].e)==0)
			{
				TS.add_clause(i,0,j,0);
				TS.add_clause(j,1,i,1);
			}
			if(judge(t[i].e-t[i].d,t[i].e,t[j].s,t[j].s+t[j].d)==0)
			{
				TS.add_clause(i,1,j,1);
				TS.add_clause(j,0,i,0);
			}
			if(judge(t[i].e-t[i].d,t[i].e,t[j].e-t[j].d,t[j].e)==0)
			{
				TS.add_clause(i,1,j,0);
				TS.add_clause(j,1,i,0);
			}
    	}
		if(!TS.solve()) printf("NO\n");
		else
    	{
        	printf("YES\n");
        	for(int i=0;i<n;i++)
       		 {
       		    // cout<<TS.mark[i*2]<<endl;
            	if(TS.mark[i*2])
               	 printf("%02d:%02d %02d:%02d\n",t[i].s/60,t[i].s%60,(t[i].s+t[i].d)/60,(t[i].s+t[i].d)%60);
            	else
                	printf("%02d:%02d %02d:%02d\n",(t[i].e-t[i].d)/60,(t[i].e-t[i].d)%60,t[i].e/60,t[i].e%60);
        	}
    	}

	}
	 return 0;


}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值