题目描述
猫咪要么睡觉,要么醒着,其连续睡眠时间不得少于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方便很多。