动态规划解决背包问题*
实验要求
给定n种物品和一个背包,背包容量为X,物品i的重量为ai,价值为bi(i=1,2,3…)。编写一个动态规划程序,使背包内的物品价值最大。
实验过程
在解决找背包问题时,有以下几个步骤。
1、建立一个查询表results,把前面容量为X(X=0,1,2,3…)的背包可以装下的物品的最大价值存入results中。
2、动态规划算法会从重量最小的物品i开始,把它放入背包中,同时背包的容量等于初始容量X1减去放入的物品质量a。因为在查询表中可以找到背包容量小于X1的所有背包可以装下的物品最大价值,所以我们可以在results中找到背包容量等于(X1-a)的背包可以装下的物品最大价值Y。
3、用Y加上物品i的价值,就可以的到目前背包所装物品的最大价值,记为Z(i)。
4、令Z(i)=P,再比较P与Z(i+1)的大小,较大的那个等于P。当所有物品依次比价结束后,当前的P就是容量为X的背包可以装下的物品最大价值。将P存入results中。下面是一个例子假设背包容量为20,艺术品有5件,求背包可以装下的物品的最大价值。
每件艺术品对应的重量与价值如下表:
建立查询表
当背包容量为二十的时候我们有五个可选方案
1、重量为2的物品的价值3,加上容量为(20-2)的背包所装的物品的最大价值,得到总价值为38。
2、重量为2的物品的价值4,加上容量为(20-3)的背包所装的物品的最大价值,得到总价值为36。
3、重量为4的物品的价值8,加上容量为(20-4)的背包所装的物品的最大价值,得到总价值为40。
4、重量为5的物品的价值3,加上容量为(20-5)的背包所装的物品的最大价值,得到总价值为36。
5、重量为9的物品的价值10,加上容量为(20-9)的背包所装的物品的最大价值,得到总价值为30。
第三个方案得到最优解。
前面仅得到背包所装物品的最大价值,还没有得出所装物品的种类和数量。所以我们还需要一个列表来帮助我们记录所装的物品的种类。通过记录查询表中每一次所加的物品可轻松拓展used,记录所加物品。如果知道上一次加的物品种类,便可以用背包的当前容量减去该物品的质量从而找到表的前一项并通过它知道之前所加的物品的种类。再通过对存放所用物品种类的列表进行遍历便可以得到背包内的物品的种类和数量。
def recDC(artlist, wightlist, valuelist, bagbig, results, used):
changeDict = {}
artDict = {}
for i in range(len(artlist)):
itemdict = {wightlist[i]: artlist[i]}
changeDict.update(itemdict)
newItemdict = {wightlist[i]: valuelist[i]}
artDict.update((newItemdict))
for item in range(bagbig+1):
count = item
newitem = 0
listj = [c for c in list(artDict.keys()) if c <= item]
for j in listj:
if results[item - j] + artDict[j] > count:
count = results[item - j] + artDict[j]
newitem = changeDict[j]
if not listj:
results[item] = 0
else:
results[item] = count
used[item] = newitem
return results[bagbig], artDict
def printitem(used, bagbig, artdict):
item = bagbig
countlist = []
while item > 0:
thisitem = used[item]
countlist.append(thisitem)
item = item - artdict[thisitem]
p = list(set(countlist))
for i in p:
a = countlist.count(i)
itemdict = {i: a}
print("物品种类:物品数量", itemdict)
测试数据
a = [1, 2, 3, 4, 5]
w = [2, 3, 4, 5, 9]
v = [3, 4, 8, 8, 10]
b = 20
o = [0]*21
h = [0]*21
s = recDC(a, w, v, b, o, h)
printitem(h, b, s[1])
print("背包可装物品最大价值为%d" % s[0])
测试结果
实验分析
1、本实验的一个重要的部分在于对数据的处理。因为实验中共有三组数据,一个是物品的类别一个是物品的重量还有一个是物品的价值。而我们需要解决的问题是在规定的重量范围内是我们能够拿到的物品的总价值最高,而且要给出所拿物品的种类及其数量,所以三组数据要一一对应起来,并且可以通过一个数据找到与它对应的其它两个数据,这样才能使程序高效的运行。众所周知,在字典这个数据结构中,我们可以快速的通过keys值查找到相应的values值,这样的数据结构正是本实验所需要的。于是,构造了changeDict和artDict,两个字典,均以重量为keys值,分别以价值和类别为values值。这在下面这段代码中极为重要。
for item in range(bagbig+1):
count = item
newitem = 0
listj = [c for c in list(artDict.keys()) if c <= item]
for j in listj:
if results[item - j] + artDict[j] > count:
count = results[item - j] + artDict[j]
newitem = changeDict[j]
if not listj:
results[item] = 0
else:
results[item] = count
used[item] = newitem
到所拿的全部物品,但并没有统计物品的种类及数量。所以需要对countlist进行一些处理。
p = len(countlist)
list1 = []
for i in range(len(countlist)):
for j in range(i + 1, len(countlist)):
if countlist[i] == countlist[j]:
countlist.pop(j)
list1.append(p - len(countlist) + 1)
print(list1)
最开始使用的是这样的方法,我以为len(countlist)会随着countlist的变化而变化,但是它只会保留最开始的值所以出现了下图所示的错误。
可以用p=list(set(countlist)去掉countlist中重复的元素,再用countlist.count(i)方法得到i在countlist中出现的次数。
p = list(set(countlist))
for i in p:
a = countlist.count(i)
itemdict = {i: a}
print(“物品种类:物品数量”, itemdict)