假设某地区有5座钻石矿,每座钻石矿的钻石储量不同,根据挖矿难度,需要参与挖掘的工人数量也不同。假设能够参与挖矿工人的总数是10人,且每座钻石矿要么全挖,要么不挖,不能只派出一部分人挖取一部分矿产。要求用程序求解出,要想得到尽可能多的钻石,应该选择挖取哪几座矿产?
矿产编号 | 钻石储量 | 所需工人数量 |
1 | 400 | 5 |
2 | 500 | 5 |
3 | 200 | 3 |
4 | 300 | 4 |
5 | 350 | 3 |
方案:
m个工人,设第n个矿所需的工人为L[n],能产钻石G[n]。在到达本矿需要人数前,产量等于前n-1各矿,F(n-1,m)
一,放弃第n个矿,产量为n-1个矿,F(n-1,m)
二,从m人中分出L[n]个人去处理第n个矿,产量为缺了L[n]个人的前n-1个矿的产量+第n个矿的产量,F(n-1,m-L[n])+G[n]
F(n,m)=F(n-1,m) (n>1,m<L[n])
F(n,m)=max(F(n-1,m),F(n-1,m-L[n])+G[n]) (n>1,m>=L[n])
矿产数量n | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 |
1 | ||||||||||
2 | ||||||||||
3 | ||||||||||
4 | ||||||||||
5 |
在挖掘第一个矿产时,由于其所需的工人数量为5,所以当m 的取值小于5时,根据公式F(n,m)=0(n≤1,m<L[0]),获得的钻石数量均为0。
当m 的取值大于或等于5时,根据公式F(n,m)=G[0](n==1,m≥L[0]),钻石数量的取值为400。
这样就确定了边界条件。
矿产数量n | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 |
1 | 0 | 0 | 0 | 0 | 400 | 400 | 400 | 400 | 400 | 400 |
2 | ||||||||||
3 | ||||||||||
4 | ||||||||||
5 |
在挖掘第2个矿产时,由于其需要5个人进行挖掘,因此当m 取值小于5时,根据公式F(n,m)=F(n-1,m)(n>1,m<L[n-1]),F(2,m)=F(1,m)=0;当m 取值大于或等于5时,根据公式F(n,m)=max{F(n-1,m),F(n-1,m-L[n-1])+G[n-1]}(n>1,m≥L[n-1]),在5~9人的区间里,获得的钻石数量为500,即所有人都去参加第2个矿产的挖掘时获得的钻石量。
这是因为当m∈{5,9}时,F(1,m)<F(1,m-5)+500,但人数只够挖掘1个矿产,故选择储量较大的矿产。而在参与人数上升为10人时,上式仍成立,但此时两个矿产可以同时挖掘,因此获得的钻石数量为900。
矿产数量n | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 |
1 | 0 | 0 | 0 | 0 | 400 | 400 | 400 | 400 | 400 | 400 |
2 | 0 | 0 | 0 | 0 | 500 | 500 | 500 | 500 | 500 | 900 |
3 | ||||||||||
4 | ||||||||||
5 |
同理,在挖掘第3个矿产时,钻石产出量为200,需要的工人数量为3。
矿产数量n | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 |
1 | 0 | 0 | 0 | 0 | 400 | 400 | 400 | 400 | 400 | 400 |
2 | 0 | 0 | 0 | 0 | 500 | 500 | 500 | 500 | 500 | 900 |
3 | 0 | 0 | 200 | 200 | 500 | 500 | 500 | 700 | 700 | 900 |
4 | ||||||||||
5 |
第4个矿产的钻石产出量为300,需要的工人数量为4。
矿产数量n | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 |
1 | 0 | 0 | 0 | 0 | 400 | 400 | 400 | 400 | 400 | 400 |
2 | 0 | 0 | 0 | 0 | 500 | 500 | 500 | 500 | 500 | 900 |
3 | 0 | 0 | 200 | 200 | 500 | 500 | 500 | 700 | 700 | 900 |
4 | 0 | 0 | 200 | 300 | 500 | 500 | 500 | 700 | 800 | 900 |
5 |
针对第5个矿产的钻石产出量。
矿产数量n | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 |
1 | 0 | 0 | 0 | 0 | 400 | 400 | 400 | 400 | 400 | 400 |
2 | 0 | 0 | 0 | 0 | 500 | 500 | 500 | 500 | 500 | 900 |
3 | 0 | 0 | 200 | 200 | 500 | 500 | 500 | 700 | 700 | 900 |
4 | 0 | 0 | 200 | 300 | 500 | 500 | 500 | 700 | 800 | 900 |
5 | 0 | 0 | 350 | 350 | 500 | 550 | 650 | 850 | 850 | 900 |
根据以上思路,在用程序实现该算法的过程中,采用自底向上的方式进行计算,像填表过程一样从左至右、从上到下逐渐获得计算结果。这样,可以不需要存储整个表格的内容,仅需要存储前一行的结果,就可以推出下一行的内容,避免了重复计算。
代码:
def goldMine(n, m, g, L):
t_results = [0 for _ in range(m + 1)] # 保存上一行结果的数组
for i in range(1, m + 1): # 填充边界格子的值,从左向右填充表格第一行的内容
if i < L[0]: # 第一个矿
t_results[i] = 0 # 若当前人数少于挖掘第一个金矿所需人数,黄金量为0
else:
t_results[i] = g[0] # 若当前人数不少于第一个金矿所需人数,黄金量为g[0]
for i in range(1, n): # 外层循环为金矿数量
results = [0 for _ in range(m + 1)] # 保存返回结果的数组
for j in range(1, m + 1): # 内层循环为矿工数量
if j < L[i]: # 是否达到该矿所需人数
results[j] = t_results[j]
else:
results[j] = max(t_results[j], t_results[j - L[i]] + g[i])
t_results = results
return t_results[-1]