【二分查找】2023B-食堂供餐
题目描述与示例
题目描述
某公司员工食堂以盒饭方式供餐。为将员工取餐排队时间降低为0
,食堂的供餐速度必须要足够快。现在需要根据以往员工取餐的统计信息,计算出一个刚好能达成排队时间为0
的最低供餐速度。即,食堂在每个单位时间内必须至少做出多少份盒饭才能满足要求。
输入
- 第
1
行为一个正整数N
,表示食堂开餐时长。1 <= N <= 1000
- 第
2
行为一个正整数M
,表示开餐前食堂已经准备好的盒饭份数。Pi <= M <= 1000
- 第
3
行为N
个正整数,用空格分隔,依次表示开餐时间内按时间顺序每个单位时间进入食堂取餐的人数Pi
1 <= i <= N
,0 <= Pi <= 100
输出
一个整数,能满足题目要求的最低供餐速度(每个单位时间需要做出多少份盒饭)。
说明
每人只取一份盒饭。需要满足排队时间为0
,必须保证取餐员工到达食堂时,食堂库存盒饭数量不少于本次来取餐的人数。第一个单位时间来取餐的员工只能取开餐前食堂准备好的盒饭。每个单位时间里制作的盒饭只能供应给后续单位时间来的取餐的员工,食堂在每个单位时间里制作的盒饭数量是相同的。
示例一
输入
3
14
10 4 5
输出
3
说明
本样例中,总共有3
批员工就餐,每批人数分别为10
、4
、5
。
开餐前食堂库存14
份。食堂每个单位时间至少要做出3
份餐饭才能达成排队时间为0
的目标。具体情况如下: 第一个单位时间来的10
位员工直接从库存取餐,取餐后库存剩余4
份盒饭,加上第一个单位时间做出的3
份,库存有7
份。
第二个单位时间来的4
员工从库存的7
份中取4
份,取餐后库存剩余3
份盒饭,加上第二个单位时间做出的3
份,库存有6
份。
第三个单位时间来的员工从库存的6
份中取5
份,库存足够。
如果食堂在单位时间只能做出2
份餐饭,则情况如下:
第一个单位时间来的10
位员工直接从库存取餐,取餐后库存剩余4
份盒饭,加上第一个单位时间做出的2
份,库存有6
份。 第二个单位时间来的4
员工从库存的6
份中取4
份,取餐后库存剩余2
份盒饭,加上第二个单位时间做出的2
份,库存有4
份。
第三个单位时间来的员工需要取5
份,但库存只有4
份,库存不够。
解题思路
二段性分析
考虑出餐速度speed
与能否完成供餐之间的关系
- 出餐速度
speed
越大的时候,越可能完成供餐。当取到最值speed = max(nums)
时,一定能完成供餐。 - 对于在区间
[1, min(nums)]
之间取值的出餐速度speed
而言,一定存在一个值ans
,使得- 当
speed ∈ [0, ans)
时,无法完成供餐。 - 当
speed ∈ [ans, max(nums)]
时,可以完成供餐。 - 这体现了这个问题的二段性,
ans
是我们需要的答案,而ans
的寻找就可以用二分查找来完成。
- 当
子问题分析
对于二分查找过程中得到的每一个出餐速度speed = mid
,我们都要去判断在出餐速度的条件下能否完成供餐。
初始化剩余餐数 rest = M
,遍历nums
数组中每一分钟前来就餐的人数num
。若
rest < num
,则无法完成供餐,直接返回False
。rest ≥ num
,该分钟可以完成供餐,rest
要减去当前分钟就餐人数num
,同时加上本分钟多提供的餐数speed
,用于后续人员的就餐。
上述逻辑整理为代码即构建 check_available(M, nums, speed)
函数
def check_available(M, nums, speed):
rest = M
for num in nums:
if rest < num:
return False
rest -= num
rest += speed
return True
代码
# 题目:2023B-食堂供餐
# 分值:200
# 作者:许老师-闭着眼睛学数理化
# 算法:二分查找
# 代码看不懂的地方,请直接在群上提问
# 该函数用于检查:当选择单位时间出餐份数为 speed 时,能否完成供餐
def check_available(M, nums, speed):
# 初始化剩余餐数为M
rest = M
# 遍历每分钟前来的人数num
for num in nums:
# 如果剩余餐数小于本分钟前来人数num
# 有员工需要等待,无法完成供餐,直接返回False
if rest < num:
return False
# 本分钟可以完成供餐,那么剩余餐数减去就餐人数
rest -= num
# 剩余餐数加上该分钟的出餐,可以提供给后续就餐的员工
rest += speed
# 在上述循环中没有返回False,说明没有出现员工需要等待的情况
# 所有人都可以无需等待完成就餐,供餐完成,返回True
return True
# N分钟
N = int(input())
# 初始已有M份餐
M = int(input())
# N分钟前来就餐的员工数目
nums = list(map(int, input().split()))
# 出餐速度最大值取max(nums),一定能够满足
# 每个员工都不需要等餐就可以就餐
left, right = 1, max(nums) + 1
# 左闭右开区间,退出循环时存在 left = right = mid
# 循环不变量为left < right
while left < right:
# 计算 left 和 right 的平均值 mid
mid = (left + right) // 2
# 可以完成供餐,说明供餐速度较大,可以缩小,搜索区间向左折半、
# 退出循环时,存在speed = left = right恰好满足check_available(M, nums, speed)
if check_available(M, nums, mid):
right = mid
else:
left = mid + 1
# 退出循环时,left = right是恰好可以完成供餐的最小速度
print(left)
时空复杂度
时间复杂度:O(NlogC)
。
C = max(nums)
为就餐人数数组nums
中的最大值。- 子问题单次求解即函数
check_available(M, nums, speed)
时间复杂度为O(N)
- 二分查找算法的时间复杂度为
O(logC))
。每次都需要调用check_available()
函数,故总的二分查找时间复杂度度为O(NlogC))
空间复杂度:O(1)
。
华为OD算法/大厂面试高频题算法练习冲刺训练
-
华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
-
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
-
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
-
60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁
-
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
-
可查看链接 OD算法冲刺训练课程表 & OD真题汇总(持续更新)
-
绿色聊天软件戳
od1336
了解更多