WEEK14 周记 限时赛——模拟题_猫睡觉问题

一、题意

1.简述

众所周知,TT家里有一只魔法喵。这只喵十分嗜睡。一睡就没有白天黑夜。喵喵一天可以睡多次!!每次想睡多久就睡多久╭(╯^╰)╮

喵睡觉的时段是连续的,即一旦喵喵开始睡觉了,就不能被打扰,不然喵会咬人哒[○・`Д´・ ○]

可以假设喵喵必须要睡眠连续不少于 A 个小时,即一旦喵喵开始睡觉了,至少连续 A 个小时内(即A*60分钟内)不能被打扰!

现在你知道喵喵很嗜睡了,它一天的时长都在吃、喝、拉、撒、睡,换句话说要么睡要么醒着滴!

众所周知,这只魔法喵很懒,和TT一样懒,它不能连续活动超过 B 个小时。

猫主子是不用工作不用写代码滴,十分舒适,所以,它是想睡就睡滴。

但是,现在猫主子有一件感兴趣的事,就是上BiliBili网站看的新番。

新番的播放时间它已经贴在床头啦(每天都用同一张时间表哦),这段时间它必须醒着!!

作为一只喵喵,它认为安排时间是很麻烦的事情,现在请你帮它安排睡觉的时间段。

2.输入格式

多组数据,多组数据,多组数据哦,每组数据的格式如下:

第1行输入三个整数,A 和 B 和 N (1 <= A <= 24, 1 <= B <= 24, 1 <= n <= 20)

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

3.输出格式

我们知道,时间管理是一项很难的活,所以你可能没有办法安排的那么好,使得这个时间段满足喵喵的要求,即每次睡必须时间连续且不少于 A 小时,每次醒必须时间连续且不大于 B 小时,还要能看完所有的番,所以输出的第一行是 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。

输出要排序吗?(输出打乱是能过的,也就是说,题目对输出的那些时间段间的顺序是没有要求的)

哦对了,喵喵告诉你说,本题是 Special Judge,如果你的输出答案和 Sample 不太一样,也可能是对的,它有一个判题程序来判定你的答案(当然,你对你自己的答案肯定也能肉眼判断)

4.样例

Input

12 12 1
23:00-01:00
3 4 3
07:00-08:00
11:00-11:09
19:00-19:59

Output

Yes
1
01:07-22:13
No

二、算法

主要思路

这个题因为有跨夜的情况,所以虽然每一天的时间表都是一样的,但需要考虑两天之间情况。据学长说将遍历的时间范围扩展到2天也就是48小时来考虑会十分容易,这里没有尝试这种思路,而是用了另外一种思路:在一天的时间范围内遍历,只不过到了一天末尾之后取模再从头开始。但事实证明取模的方法容易出bug。


采取能睡就睡的策略。
将所有的番段都记录下来存到一个 v e c t o r vector vector中去,并将时:分的格式转化为分钟。
全记录下来之后按番段开始时间点排序(因为番段保证没有重合所以按开始时间或者是结束时间排序实际上都是一样的,只不过按结束时间的话跨夜的番段将在最前面)。
之后进入循环,依次取出 v e c t o r vector vector中的番段,计算其开始时间点与 s t st st(记录了前一个番段的结束时间+1,也即空闲段的开始时间点)的距离,也即空闲时间段的长度。如果这个距离小于睡觉的最短时间,则这个时间不能用来睡觉必须工作,另外用于一个变量 w o r k t i m e worktime worktime将这个时间记录下来,意义是记录一个累加的连续工作时间长度。然后判断这个工作时间是否超过了最长工作时间,如果超过了就说明不存在一个合理的安排,算法结束。如果不超过则继续。
之后计算该番段的时间,累加到 w o r k t i m e worktime worktime上,继续判断worktime的长度是否满足要求。如果满足要求则重新取一个番段,开启新一次的for循环。
前面还一种情况没有讨论,就是距离不短于睡觉最短时间,也就空闲段能够用来睡觉,那么前面曾经记录的 w o r k t i m e worktime worktime就应该清0,因为工作时间不再连续了。之后将这个睡觉时间记录下来。
当一个循环之后,又第2次到达了最初的那个番段,这个时候标记一下已经第2个循环了,这时候如果再碰到一个睡眠正常的空闲段,那么就该直接停止了,那么此时也是能够有合理时间安排的情况。


为什么要取模进入第2天?因为要判断第一天的最后一个番段(跨夜的番段当做第一天的番段)的结束点与第2天的第一个番段的开始点之间的空闲段是否符合要求。假如说前面都没有工作超时的情况,但这里我们讨论的空闲段不符合要求,那么这个空闲段就应该工作,那么这个空闲段就要开始往后累加,从第2天的第一个番段开始,这种累加的情况是我们在第一天遍历的时候没有遇到过的,而且有可能会出现超出工作时间要求的情况。所以需要考虑。


这个题的思路不难理解,但是在取模方面很容易出bug。
比如计算空闲段的长度,lth = (1440 + v[i].st - st)%1440; 这里要加上一个1440,因为有可能减出来是个负数,而在计算机里负数取模还是负数。
再比如output(st,(1440+v[i].st-1)%1440);,这是当空闲段可以用来睡眠时记录时间的一个函数,睡眠起始时间为 s t st st,但是结束时间要 + 1440 +1440 +1440再取模,因为存在一个 v [ i ] . s t − 1 v[i].st-1 v[i].st1这样一个 − 1 -1 1的情况,就可能出现负数,当时就是这里出现负数而WA。
再比如计算番段长度lth = (1440 + v[i].ed - v[i].st)%1440 + 1;当时 + 1 +1 +1写在了里面,对于 00 : 00 − 23 : 59 00:00-23:59 00:0023:59这样的一个番段来说,时间长度就是0了,这显然是错误的。


三、代码

//正确代码 
#include<iostream>
#include<cstdio>
#include<sstream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdlib> 
#include<vector>
#include<algorithm>
using namespace std;
int slpdn;
int workup;
//pair<int,int> tpoint[100]; //<i,color>
struct wt{
	int st,ed;
	wt(int st,int ed):st(st),ed(ed){}
	bool operator<(const wt& b)const{return st<b.st;}
};
vector<wt> v; //<st,ed>

int index = 0;
struct tp{
	int h1;
	int h2;
	int m1;
	int m2;
	tp(int h1,int m1,int h2,int m2):h1(h1),m1(m1),h2(h2),m2(m2){}
};
vector<tp> out;
void output(int st,int ed){
	int h1 = st/60;
	int m1 = st%60;
	int h2 = ed/60;
	int m2 = ed%60;
	out.push_back(tp(h1,m1,h2,m2));
}
int main(){
	int a,b,n;
	while(scanf("%d%d%d",&a,&b,&n)!=EOF){
		v.clear();
		out.clear();
		slpdn = a*60;
		workup = b*60;
		int mintime = 1441;  //最早的工作起点 
		for(int i=0;i<n;i++){
			int h1,m1,h2,m2;
			scanf("%d:%d-%d:%d", &h1, &m1, &h2, &m2);
			int time1 = h1*60 + m1;
			int time2 = h2*60 + m2;
			v.push_back(wt(time1,time2));
			mintime = mintime<time1?mintime:time1;
		}
		
		sort(v.begin(),v.end());  //将番段以起始时间排序 
		int isok = true;  //是否能够安排得过来 
		int st = mintime;  //循环的起始位置 
		int worktime = 0;  //临时存储一个工作时间段的长度 
		int cnt = 0;  //第几个循环 
		for(int i=0;;i=(i+1)%v.size()){	
			int lth = (1440 + v[i].st - st)%1440;  //工作前的空闲时间长度
			if(lth < slpdn){  //睡眠时间过短 
				worktime += lth;
				if(worktime > workup){
					isok = 0; 
					break;
				}
			} 
			else{  //睡眠时间正常 
				worktime = 0;
				if(cnt>=2)
					break;
				output(st,(1440+v[i].st-1)%1440);  //要不要取模,取模的时候会不会出现负数,一定注意啊啊啊啊啊 
			}
			st = (v[i].ed+1)%1440;  //更新st 
			lth = (1440 + v[i].ed - v[i].st)%1440 + 1;  //这里出过问题 
			worktime += lth;
			if(worktime > workup){
				isok = 0; 
				break;
			}
			if(i==0) cnt++;
		}
		
		if(!isok){
			printf("No\n");
		}
		else{
			printf("Yes\n%d\n",out.size());
			for(int i=0;i<out.size();i++){
				printf("%02d:%02d-%02d:%02d\n",out[i].h1,out[i].m1,out[i].h2,out[i].m2);
			}
		}
	} 
	return 0;
}
/*
4 4 5
01:00-03:00
04:00-04:59
12:00-13:00
14:00-15:00
23:30-00:01
3 24 3
08:00-12:00
15:00-15:30
16:00-06:30
4 4 4
01:00-03:00
04:00-04:59
12:00-13:00
14:00-15:00
1 24 1
00:00-23:59

12 12 1
23:00-01:00
3 4 3
07:00-08:00
11:00-11:09
19:00-19:59

//
Yes
1
01:07-22:13
No
*/
/*
3 6 4
07:00-08:00
9:00-12:00
16:00-19:00
23:00-3:59
//
Yes
3
12:01-15:59
19:01-22:59
04:00-06:59

3 6 5
1:00-2:00
2:01-2:20
2:30-07:00
10:20-11:40
18:46-19:10

1 24 1
00:00-13:00 
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值