POJ 3683 Priest John's Busiest Day

题目链接 : POJ3683

Priest John's Busiest Day
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 6797 Accepted: 2318 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 yearN couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from timeSi to timeTi. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. Thei-th couple needDi 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 fromSi toSi +Di, or fromTi -Di toTi). 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 Si,Ti andDi.Si andTi are in the format ofhh: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 anotherN 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

 
2-sat 判断可解性并且输出路径的题
没有用拓扑排序来弄 利用了一个2-sat的对称来输出路径的(大概证了一下 应该没问题)
具体细节看代码  个人感觉已经很优化了 没了拓扑排序的很多常数  然后跑tarjan的时候也尽量优化了。。
不知道为啥还是407MS。。那些几十MS的  估计不是用tarjan跑的强连通分量吧  不过会差那么多么。。看不懂。
 
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
using namespace std;



struct p
{
    int x,y;
}ar[2005];
vector<int> gra[2005];
bool vis[2005];
int stack[2005],k;
int low[2005],dfn[2005],scc[2005];
int timer,cnt;
void tarjan_scc(int s)
{
    vis[s]=1;
    low[s]=dfn[s]=++timer;
    stack[k++]=s;
    int to;
    for(int i=0;i<gra[s].size();i++)
    {
        to=gra[s][i];
        if(dfn[to]==0)
        {
            tarjan_scc(to);
            low[s]=min(low[to],low[s]);
        }
        else if(vis[to]) low[s]=min(dfn[to],low[s]);
    }
    if(dfn[s]==low[s])
    {
        do
        {
            to=stack[--k];
            vis[to]=0;
            scc[to]=cnt;
        }while(stack[k]!=s);
        cnt++;
    }
}
int main()
{
    freopen("1.txt","r",stdin);
    int n,x,x1,y,y1,c;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d:%d%d:%d%d",&x,&x1,&y,&y1,&c);
        x=x*60+x1;y=y*60+y1;
        ar[i].x=x;ar[i].y=x+c;
        ar[i+n].x=y-c;ar[i+n].y=y;
    }
    for(int i=0;i<2*n;i++)                  //建图
        for(int j=i+1;j<2*n;j++)
        {
            if(ar[i].y<=ar[j].x) continue;
            else if(ar[i].x>=ar[j].y) continue;
            else
            {
                x=(i+n>=2*n)?i-n:i+n;
                y=(j+n>=2*n)?j-n:j+n;
                gra[i].push_back(y);
                gra[j].push_back(x);
            }
        }
    cnt=1;timer=0;
    bool flag=1;
    for(int i=0;i<n;i++)
    {
        if(scc[i]==0 && scc[i+n]==0) tarjan_scc(i); 
        //优化  当同一事件两个都没有归属的强连通分量的时候才tarjan 
        //证明 : 如果会有无解的情况  那么只用跑其中一个就能得出 scc[i]==scc[i+n] 这时只用跑一次
        //  如果是有解的 那么也只用知道i或i+n的强连通分量归属就足够输出解了(因为只用取一组解,和后面输出解有联系)
        if(scc[i]==scc[i+n])  
        //跑完后发现有无解的情况 如果无解的话 一定能在i处发现 不可能在i之后的点才出现
        {
            flag=0;
            break;
        }
    }
    if(flag)
    {
        printf("YES\n");
        for(int i=0;i<n;i++)
        {
            if(scc[i] && scc[i+n])    
            //如果一对点都标记有强连通分量的时候  只有一种可能导致这种情况
            //首先我们假设 前面的区间 在同一边 后面的区间 在另一边  然后建图就会出现两种边
            //一种是连接两边的边  一种是连接同一边的边  很容易知道 这两种边单独出现在图里面都不可能产生
            //一对点都会被标记的可能  只有这两种边都出现才可能 并且如果有解的话  这两个点的scc都不一样
            //那么就很简单了  如果scc[i]是比scc[i+n]大的 说明选i就必须选i+n  这里综合了tarjan的写法和2-sat的建图原理
            //简单解释一下 也就是 如果scc[i]是比scc[i+n]大的 那么跑tarjan的时候 一定是i+n先跑完 
            //反应在图上就是 i是i+n的祖先  2-sat图 里面 x是y的祖先 的意义就是 选了x必选y
            //所以就只能选i+n点 否则会面临都选的情况   而选了这个点对别的点的约束反应在tarjan跑了那个点上面了
            {
                if(scc[i+n]<scc[i]) x=ar[i+n].x,y=ar[i+n].y;
                else x=ar[i].x,y=ar[i].y;
            }
            else
            //如果只有一个点标记有强连通分量的时候  只能取那个被标记的点为答案
            //因为只有这个点才满足别的点的拓扑约束(这里充分利用了前面不把两个点都标记的步骤)
            //还有一个原因是另一个点没标记该怎么选啊wwww 
            {
                if(scc[i+n]) x=ar[i+n].x,y=ar[i+n].y;
                if(scc[i]) x=ar[i].x,y=ar[i].y;
            }
            printf("%02d:%02d %02d:%02d\n",x/60,x%60,y/60,y%60);
        }
    }
    else printf("NO\n");
    return 0;
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值