动态规划-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()