程序设计Week14大模拟——A-猫睡觉问题

题目描述

猫咪要么睡觉,要么醒着,其连续睡眠时间不得少于A小时,同时活动时间不得超过B小时,先给出一个新番时间表,猫咪在新番播出时间必须醒着,安排猫咪睡觉时间。

Input

第一行三个整数A、B、N,1 < = A,B < = 24; 1 < = N < = 20;
接下来n行为时间表,格式为:hh:mm-hh:mm (闭区间)
hh:mm 的范围为 00:00 到 23:59。注意一下,时间段是保证不重叠的,但是可能出现跨夜的新番,即新番的开始时间点大于结束时间点。
保证每个时间段的开始时间点和结束时间点不一样,即不可能出现类似 08:00-08:00 这种的时间段。时长的计算由于是闭区间所以也是有点坑的,比如 12:00-13:59 的时长就是 120 分钟。
不保证输入的新番时间表有序。

Output

第一行输出Yes或No,代表能否安排出符合要求的时间;
第2行输出一个整数 k,代表当天有多少个时间段要睡觉;
接下来 k 行是喵喵的睡觉时间段,每行一个时间段,格式形如 hh:mm-hh:mm (闭区间),这个在前面也有定义。注意一下,如果喵喵的睡眠时段跨越当天到达了明天,比如从23点50分睡到0点40分,那就输出23:50-00:40,如果从今晚23:50睡到明天早上7:30,那就输出23:50-07:30

解题思路

这道模拟和之前的那道,安排会议的模拟题很相似,其第一要点都是讲时间规范化,即自定义时间结构体。本题中,时间很好定义,用两个整数h、m记录小时和分钟,同时,由于给出的和所求的都是时间段,需要用一个结构体来存储时间段的信息,另外,为了更方便操作与直观理解,可以在结构体内重构一些符号、定义一些函数,来记录一些操作。
对于输入的新番时间表,逐条记录并加以排序;接下来就对已有序的时间段逐个加以判断:如果两个时间段中间的间隔小于A,即不满足猫咪连续睡眠要求,则说明这个间隔内猫咪依然是清醒的,因此可以将这两个时间段合并为一个(同时总时间段数-1);否则,则说明该间隔内猫咪是能睡觉的,不采取合并。
在合并操作完成后,对当前记录时间段的数组tp进行判断:

  • 如果其长度为1,意味着整个活动时间仅有一个时间段,需要判断该活动时间是否超过B小时,睡眠时间是否小于A小时,根据题目要求来输出即可;
  • 如果其长度大于1,需要考虑第一个时间段和最后一个时间段的关系,因为题目明确说明最后一个时间段的结束时间可能是第二天,由于我采用24h记时,因此需要加一个特判,同时题目也明确不存在重叠的时间段,因此在时间相减时不会出现负数,如果这两者的间隔依然小于A,则这两个时间段进行合并,在对每个时间段的长度与B进行比较,根据比较结果来按照题目要求输出。

实现代码

#include <iostream>
#include <algorithm>
using namespace std;

struct time
{
    time(int hh=0,int mm=0)
    {
    	h=hh;
    	m=mm;
	}
    time operator- (const time& p)
	{
        time t(h,m);
        if(t<p)
			t.h+=24;
        if(t.m<p.m)
		{
			t.h-=1; 
			t.m+=60;
		}
        t.m-=p.m;
		t.h-=p.h;
        return t;
    }
    bool operator< (const time& p)
	{
		if(h!=p.h)
			return h<p.h;
		return m<p.m;
    }
    time& operator= (const time& p)
	{
		h=p.h;
		m=p.m;
		return *this;
	}
	
    int h,m;
};

time sub1(time& p)
{
    time t=p;
    if(t.m==0)
	{
        if(t.h==0)
			t.h+=24;
        t.h--;
		t.m+=60;
    }
    t.m--;
    return t;
}

time add1(time& p)
{
    time t=p;
    t.m+=1;
    if(t.m==60)
	{
        t.m=0;
		t.h++;
    }
    if(t.h==24)
		t.h=0;
    return t;
}

struct timep
{
    timep(){}
    timep(struct time s,struct time e)
	{
        st=s; 
		et=e;
    };
    bool operator< (const timep& p)
	{return st<p.st;}
    timep& operator= (const timep& p)
	{
		st=p.st; 
		et=p.et; 
		return *this;
	}
    time st,et;
};

timep tp[25];

int main()
{
	int a,b,n;
    while(scanf("%d %d %d",&a,&b,&n)!=EOF)
	{
        time A(a,1),B(b-1,59);
        bool flag=0;
		int cnt=0; 
        for(int i=1;i<=n;i++)
		{
            int a1,a2,a3,a4;
            scanf("%d:%d-%d:%d",&a1,&a2,&a3,&a4);
            time t1(a1,a2),t2(a3,a4);
            tp[++cnt].st=t1;
			tp[cnt].et=t2;
        }
        sort(tp+1,tp+1+cnt);
        for(int i=1;i<cnt;i++)
		{
            if(tp[i+1].st-tp[i].et<A)
			{
                tp[i].et=tp[i+1].et;
                for(int j=i+1;j<cnt;j++)
                    tp[j]=tp[j+1];
                cnt--;
				i--;
            }
        }
        if(cnt!=1)
		{
			if(tp[1].st-tp[cnt].et<A)
			{
            	tp[cnt].et=tp[1].et;
            	for(int i=1;i<cnt;i++)
					tp[i]=tp[i+1];
            	cnt--;
    		}
		}
        for(int i=1;i<=cnt;i++)
		{
            if(B<tp[i].et-tp[i].st)
			{
                flag=1;
				break;
            }
        }
        if(flag)
		{
            printf("No\n");
			continue;
        }
        if(cnt==1)
		{
			if(tp[1].st-tp[1].et<A)
			{
	    		printf("No\n");
				continue;
    		}
		}
        printf("Yes\n%d\n",cnt);
        for(int i=1;i<=cnt;i++)
		{
            time t1=add1(tp[i%cnt==0?cnt:i%cnt].et);
            time t2=sub1(tp[(i+1)%cnt==0?cnt:(i+1)%cnt].st);
            printf("%02d:%02d-%02d:%02d\n",t1.h,t1.m,t2.h,t2.m);
        }
    }
    return 0;
}

总结

这道题目,对于时间存储与排序在之前的题目中已经出现过,最关键的点在于合并时间段,将所有不满足条件的时间段加以合并,再逐一进行判断,会使得整个题目思路更加清晰。
最后在输入和输出数据的时候,使用printf的占位符会比cin、cout方便很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值