动态规划-0-1背包问题

0-1背包问题:有N件物品和一个容积为M的背包。第i件物品的体积w[i],价值是d[i]。
求解将哪些物品装入背包可使价值总和最大。每种物品只有一件,可以选择放或者不放
(N<=3500,M <= 13000)。
思路:用 F[i][j] 表示取前i种物品,使它们总体积不超过j的最优取法取得的价值总和。要求F[N][M]
就取1种物品,如果w[1]体积小于j,那么可以放入背包,价值为d[1],
如果放不下,那么价值就是0。
边界:if (w[1] <= j)
F[1][j] = d[1];
else
F[1][j] = 0;
用 F[i][j] 表示取前i种物品,使它们总体积不超过j的最优取法取得的价值总和
递推: F[i][j] = max(F[i-1][j],F[i-1][j-w[i]]+d[i])
取或不取第 i种物品,两者选优
(j-w[i] >= 0才有第二项)
本题如用记忆型递归,需要一个很大的二维数组,会超内存。注意到这个二维数组的下一行的值,
只用到了上一行的正上方及左边的值,因此可用滚动数组的思想,只要一行即可。即可以用一维数组,
用“人人为我”递推型动归实现。
令V(i,j)表示当前背包为容量 j,前 i 个物品最佳组合对应的价值最优解,那么就有两种可能:
1.包的容量比该商品体积小,即j < Vi,装不下第i个物品。此时背包容量为j,前i个物品的最优解
与背包容量为j,前i-1个物品最佳组合对应的最优解是一样的,即V(i,j)=V(i-1,j)
2.当包的容量等于或大于该商品的体积,即j >= Vi,能装下第i个物品。那无非有两种情况,
在前i个物品,背包容量为j的最优组合里有第i个物品,但同时占据了背包Wj个容量,
V(i,j) = V(i-1,j-Wi)+Vi;

通过上面的方法可以求出背包问题的最优解,但还不知道这个最优解由哪些商品组成,故要根据
最优解回溯找出解的组成,根据填表的原理可以有如下的寻解方式:
V(i,j)=V(i-1,j)时,说明没有选择第i 个商品,则回到V(i-1,j);
V(i,j)!=V(i-1,j)时,说明装了第i个商品,该商品是最优解组成的一部分,
一定有V(i,j)=V(i-1,j-w(i))+v(i),随后我们得回到装该商品之前,即回到V(i-1,j-w(i));
重复1,2,一直遍历到i=0结束为止,所有解的组成都会找到。

参考:https://blog.csdn.net/ggdhs/article/details/90648890
https://blog.csdn.net/qq_34178562/article/details/79959380

python算法实现:

 1 # 该方法有一个弊端,比如体积是10,我们的列就从0至10去做循环,这样如果题目要求有13000这样的
 2 # 数值,二维数组太大了,运行效率极低,甚至可能出现内存不足的情况,因此需要改进算法
 3 def zeroOneBag1(num, capacity, weightList, valueList):
 4     # 二维数组初始化,行表示物品,列表示最大价值
 5     # valueExcel[0][j],valueExcel[i][0]边界条件都是0
 6     # valueExcel[0][j]表示前0种物品,那就是没有选择物品,所以最大价值是0
 7     # valueExcel[i][0]表示背包总容量是0,那就是什么物品都没法装入,因此价值也是0
 8     valueExcel = [[0 for j in range(capacity + 1)] for i in range(num + 1)]
 9     for i in range(1, num + 1):
10         for j in range(1, capacity + 1):
11             # 不选该物品的情况下,等于同列上一行的值
12             valueExcel[i][j] = valueExcel[i-1][j]
13             # 背包总容量能够放当前物体,那么就需要用总容量-当前物品的体积,得到剩余的体积,
14             # 那么valueExcel[i-1][j-weightList[i-1]]表示没有加入该物品,该剩余体积值下的最大价值
15             # 加上该物品的价值后,那就是valueExcel[i-1][j-weightList[i-1]]+valueList[i-1]
16             # 表示加入该物品后,在该体积下的最大价值,这时为了去整体的最优,就需要与不加入该物品时,
17             # 那两个的价值是最大的,然后填入该二维数组内,已保证二维数组中的值都是在[i][j]下都是最优解
18             if j >= weightList[i-1]:
19                 valueExcel[i][j] = max(valueExcel[i][j], valueExcel[i-1][j-weightList[i-1]]+valueList[i-1])
20 
21     #for x in valueExcel:
22         #print(x)
23 
24     return valueExcel
25 
26 
27 # 背包价值最大化的情况下,选择了哪些物品
28 
29 def show(num, capacity, weightList, valueExcel):
30     print('最大价值为:', valueExcel[-1][-1])
31     # 是否选择该物品的list,[False, False, False, False, False, False]
32     x = [False for i in range(num)]
33     j = capacity
34     for i in range(num, 0, -1):
35         # 如果valueExcel[i][j]大于上一行同列的值,说明选择了i行物品
36         if valueExcel[i][j] > valueExcel[i - 1][j]:
37             x[i - 1] = True
38             j -= weightList[i - 1]
39     print('背包中所装物品为:')
40     for i in range(num):
41         if x[i]:
42             print('', i+1, '个,', end='')
43 
44 # valueExcel可以利用递推,实现只需要一维数组即可,思路如下:
45 # valueExcel[i][j]值替换valueExcel[i-1][j]的值,这样从而保证就一维数组即可
46 # 计算valueExcel[i][j]值可能需要比j小的值,也就是valueExcel[i-1][<j],因此
47 # 我们递推数组的时候,必须从右边开始递推
48 # 利用递推后,因为没存储中间结果,因此就没法找出最大价值下选择了哪些物品
49 def zeroOneBag2(num, capacity, weightList, valueList):
50     valueExcel = [0 for i in range(capacity + 1)]
51     for i in range(1, num + 1):
52         for j in range(capacity, 0, -1):
53             # 背包总容量够放当前物体,遍历前一个状态考虑是否置换,这里的value[j]即为上一次最佳结果
54             if j >= weightList[i-1]:
55                 valueExcel[j] = max(valueExcel[j-weightList[i-1]]+valueList[i-1], valueExcel[j])
56 
57     print(valueExcel)
58     return valueExcel[-1]
59 
60 def main():
61     # 多少个物品
62     num = 6
63     # 背包的总容量
64     capacity = 10
65     # 每个物品的重量
66     weightList = [2, 2, 3, 1, 5, 2]
67     # 每个物品的价值
68     valueList = [2, 3, 1, 5, 4, 3]
69     valueExcel = zeroOneBag1(num, capacity, weightList, valueList)
70     show(num, capacity, weightList, valueExcel)
71     #print("物品装入背包可使价值总和最大为:%d"%maxValue)
72 
73 if __name__ == '__main__':
74     main()
原文地址:https://www.cnblogs.com/an-wl/p/13152739.html