问题描述
给定一段长度为n英寸的钢条和一个价格表 pi (i = 1,2,……,n ) , 求切割钢条方案,使得收益 rn 最大。
分析问题
随便给定一个价格表
长度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
价格 pi | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 30 |
得出以下结果
钢条长度n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
收益 ri | 1 | 5 | 8 | 10 | 13 | 17 | 18 | 22 | 25 | 30 |
切割方案 | 1 | 2 | 3 | 2+2 | 2+3 | 6 | 1+6或2+2+3 | 2+6 | 3+6 | 10 |
可以看出该问题满足最优子结构性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。比如长度为7的钢条可以切割为长度为1及长度为6的两段钢条,很明显长度为6的钢条可以继续切割,也可以不切割,所以要求解出长度为7的钢条得到最多收益的切割方案首先得求解出长度为6的最佳切割方案,此时称长度为6的钢条切割问题为长度为7的钢条切割问题的子问题。
到这里可能有的人会想到用递归算法来解决问题,不过对这个问题递归算法恰恰是不可取的,当n稍微大一些,比如n=50,程序就要运行挺久时间了,因为程序作了很多无用功,及对同一个子问题多次求解,由上面的表格可以看出长度为7,8,9的钢条切割问题都依赖于长度为6的钢条切割问题,用递归算法将多次求解长度为6的钢条切割问题。
自然地我们应该想到将长度为6的钢条切割方案保存起来,而不是重复地进行求解。
这就是所谓的动态规划方法:付出额外的内存空间来节省计算时间,是典型的时空权衡的例子。
动态规划有两种等价的实现方法:带备忘的自顶向下法(如上所述,和递归类似,只是将结果保存下来,以免重复求解造成资源浪费)和自底向上法(将子问题按规模n排序,按由小至大的顺序进行求解)。
解决问题
下面给出自底向上的求解钢条切割问题的python实现。
#coding=utf-8
file=open("result.txt","w")
import random
n=20
data=[]
for i in range(n):
data.append(random.randint(0,10000))
file.write("长度由1到"+str(len(data))+"的钢条的价格分别为:"+str(data)+"\n")
s=[0 for i in range(len(data)+1)]
def bottomUpCutRod(p):
r=[-1 for i in range(len(p)+1)]
r[0]=0
for j in range(1,len(p)+1):
for i in range(1,j+1):
if r[j]<p[i-1]+r[j-i]:
r[j]=p[i-1]+r[j-i]
s[j]=i
return r[len(p)]
file.write("最大收益为:"+str(bottomUpCutRod(data))+"\n")
length=[]
def detail(total,cut):
if total==cut:
return 0
if detail(cut,s[cut])==0:
length.append(cut)
if detail(total-cut,s[total-cut])==0:
length.append(total-cut)
return 1
if detail(len(data),s[len(data)])==0 :
file.write("不切割")
else:
file.write("切割出的钢条长度为分别:"+str(length))
file.close()