一、引言
俗语说的好:春天不是读书天,夏日炎炎正好眠。秋有蚊虫冬打盹,收拾书包待明年。眼见太阳一天天的见高,正准备学学诸葛先生,实践一下草堂春睡足,小朋友又来烦我了。
题目如下:
一个小偷面前有一堆(n个)财宝,每个财宝有重量w和价值v两种属性,而他的背包只能携带一定重量m的财宝,在已知所有财宝的重量和价值的情况下,如何选取财宝,可以最大限度的利用当前的背包容量,取得最大价值的财宝(或求出能够获取财宝价值的最大值)。
二、问题分析和转化
该问题是典型的0-1背包问题,是常规考察动态规划应用的题目。但若是我用动态规划(动态规划将在后面的文章中分析)来解决,这几天别想清闲了。为便于说明,我们用两个长度为n的数组wTbl、vTbl表示各个财宝的重量和价值。此问题最原始的解法可通过如下三步来处理的。
1 以数组wTbl的全部元素为全集,求该全集的子集Ai构成的集合A
2 计算出A中Ai的重量、对应的价值累加和
3 找到2计算的重量不超标,价值最大的特定Ai。
三、问题解决
1 构建数组wTbl的子集(或者子序列)
一个简单的方法是使用itertools库中提供了combinations方法可以轻松的实现排列组合。简单的示例代码如下:
from itertools import combinations
wTblList=[1,2,3]
sublist = []
for i in range(1,len(wTblList)+1):
sublist.extend(combinations(wTblList,i)) #extend末尾增加一个数据集合,append增加一个数据项目
print(sublist)
系统输出如下:
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
当然,也可以用for循环加递归的方法求子序列,限于篇幅,不在此处展开。
2 计算所有子集的重量和价值。
计算重量较为简单,即对该子集元素求和;计算价值需要根据总量找到其所对应的价值做累加即可(当然也可能出现有重量相同,价值不同的情况,开始前只需把所有物品编制序号,把重量和价值组合存入dict的value即可),通过将重量和价值作为dict的key和value,也很容易求出对应价值。示例代码如下:
from itertools import combinations
wTblList=[6,5,7,4]
vTblList=[15,12,18,10]
dict1=dict(zip(wTblList,vTblList))
vsubSum=[]
wsubSum=[]
sublist = []
for i in range(1,len(wTblList)+1):
sublist.extend(combinations(wTblList,i)) #extend末尾增加一个数据集合,append增加一个数据项目
#求所有子集的重量数组和价值数组
for item in sublist:
vval=0
wval = 0
for witem in item:
wval =wval +witem
vval =vval +dict1[witem]
vsubSum.append(vval)
wsubSum.append(wval)
print(sublist)
print(wsubSum)
print(vsubSum)
3 根据指定的最大重量,求出价值最高的组合。
此过程较为简单,只需要从小于最大重量的重量数组中求出价值数组对应位置的最大值即可。代码如下:
from itertools import combinations
wTblList=[6,5,7,4]
vTblList=[15,12,18,10]
wMax=18
dict1=dict(zip(wTblList,vTblList))
vsubSum=[]
wsubSum=[]
sublist = []
for i in range(1,len(wTblList)+1):
sublist.extend(combinations(wTblList,i)) #extend末尾增加一个数据集合,append增加一个数据项目
#求重量数组和价值数组
for item in sublist:
vval=0
wval = 0
for witem in item:
wval =wval +witem
vval =vval +dict1[witem]
vsubSum.append(vval)
wsubSum.append(wval)
#求最优选择
prio=0
vMax=0
for i in range(len(wsubSum)):
if wsubSum[i]<=wMax and vsubSum[i]>vMax:
prio=i
vMax=vsubSum[i]
print("vMax=",vMax)
print(sublist[prio])
四、附记
1 子序列
对子序列,不使用递归和库,依然是可以求出的,典型的方法是利用类二进制的方法。典型代码如下:
def sub(arr):
finish=[] # the list containing all the subsequences of the specified sequence
size = len(arr) # the number of elements in the specified sequence
end = 1 << size # end=2**size
for index in range(end):
array = [] # remember to clear the list before each loop
for j in range(size):
if (index >> j) % 2: # this result is 1, so do not have to write ==
array.append(arr[j])
finish.append(array)
return finish
2 字符串子串
和子序列不同,所有字串中的元素依然保持父串的邻居关系。典型代码如下:
def cut(s:str):
results = []
num = 0
# x + 1 表示子字符串长度
for x in range(len(s)):
# i 表示偏移量
for i in range(len(s) - x):
results.append(s[i:i + x + 1])
return results
aa="abcd"
print(aa)
print(cut(aa))
系统输出如下:
abcd
['a', 'b', 'c', 'd', 'ab', 'bc', 'cd', 'abc', 'bcd', 'abcd']
从输出可以看出,字串中没有acd