蛋糕工厂产能规划
问题描述
小明的蛋糕工厂每天可以生产的蛋糕数量是由工厂中的机器和工人的数量决定的,即 。现在他收到了一个大订单,需要尽快生产出 个蛋糕。为了提升生产速度,小明可以使用每天生产的蛋糕去购买额外的机器或工人,每台机器或每个工人的成本是 个蛋糕。
本题的输入有初始的机器数和工人数m,w,工人或机器的成本p,生产的总蛋糕n,输出完成加工的最短时间。
题目分析
这道题目是一道动态规划题目,我们需要考虑每一天的策略,即购买多少工人、机器才能使得最终我们需要花费的时间最短。
由于本题购买的机器和工人在购买后就不再会消失,所以工人和机器越早买,它最终能够产生的蛋糕就越多。所以我们可以这样考虑:若购买这个工人或者机器最终产生的收益大于购买这个工人或机器需要的成本,此时我们就去购买它,反之就不购买它。
接着我们观察购买机器或者工人产生的收益,我们可以发现产生的收益并非线性的,而是和m,w的大小有关的,同时我们发现,购买机器和工人的收益曲线近似于,而成本曲线则近似于,若是购买机器和工人更有利的情况,则我们把蛋糕全部用来购买机器和工人的收益最大。
但是同样,我们也可以想象出来一个问题,若是每天我们生产的蛋糕全部用来购买机器和工人,而非存储起来最终交付n个蛋糕。举个例子,起始时,我们约定为我们目前剩余的蛋糕数,则此时,我们可以这样操作:
- 第一天:c += 50,全用来购买机器和工人,w += 1 最终 m = 10, w = 6, c = 0
- 第二天: c += 60,全用来购买机器和工人, w += 1,最终m = 10,w = 7, c = 10
- 第三天: c += 70,全用来购买机器和工人,w += 1,最终 m = 10, w = 8, c = 30。
- 第四天:c += 80,全部存储起来,最终 c = 110完成订单。
当然我们也可以这样操作
- 第一天:c += 50,全部存储起来,最终 c = 50
- 第二天:c += 50,全部存储起来,最终 c = 100完成订单。
可以发现这种情况下明显不购买机器和工人而是存储起来收益更大。所以,综合上面说的,我们在这道题目中每天可以使用下面这两种策略:
- 把蛋糕全部用来购买机器和工人
- 把蛋糕全部存储起来
接着我们考虑如何购买机器和工人最合适
由于,而且成本相同,所以当或时产量最大,当我们选择这个策略时,每日更新更新m,w,c数量的代码如下
buy_num = int((m*w+c)/p)
# 更新蛋糕数量
if (m*w+c) % p != 0:
c_next = m*w+c - buy_num * p
else:
c_next = 0
# 更新工人、机器的数量
if m + buy_num <= w:
m_next = m + buy_num
w_next = w
elif w + buy_num <= m:
m_next = m
w_next = w + buy_num
else:
m_next = int((m+w+buy_num)/2)
w_next = m+w+buy_num - m_next
接着我们讨论最后一天的情形,最后一天必然会将所有的蛋糕存储起来,不会用于购买机器或工人,所以代码如下
if n-c <= m * w:
return 1
# return 1表示需要1天生产,这正是最后一天的情况
接着我们考虑如何设计我们的函数,本题的solution函数要求我们返回一个需要的最小天数。而如果对应每一天我们调用的函数,我们都按照得到最小天数的策略设计,那么每一天返回的都是从这一天开始需要的最小的天数,此时我们只需要将这个天数加一就可以得到它的前一天的最小天数,这正是递归的思想。
我们按照递归的思想设计我们递归使用的函数,
def solution_buy(m, w, p, n, c):
'''
两种操作:不买机器和工人,以及尽可能买机器和工人
比较出最小值即可
'''
if 是否最后一天:
return 1
else:
# 计算出买机器和工人后,m,w,n,c的值
...
# 直接使用min比较是存储起来时间最短还是购买机器和工人时间最短,得到的最小值+1即可
return 1+min(solution_buy(m,w,p,n,c+m*w), solution_buy(m_next,w_next,p,n,c_next))
然后在solution函数中调用这个可以递归的函数
def solution(m, w, p, n):
return solution_buy(m,w,p,n,0)
如此,我们就得到了这个问题的正确答案。