11
22leetcode上没有纯01背包的问题,都是需要转化为01背包的题目,所以我先把通过纯01背包问题,把01背包原理讲清楚,后序讲解leetcode题目的时候,重点就是如何转化为01背包问题了。
3- ##01 背包
3+
4+ #01 背包
45
56有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[ i] ,得到的价值是value[ i] 。** 每件物品只能用一次** ,求解将哪些物品装入背包里物品价值总和最大。
67
@@ -30,7 +31,7 @@ leetcode上没有纯01背包的问题,都是需要转化为01背包的题目
3031
3132以下讲解和图示中出现的数字都是以这个例子为例。
3233
33- * 确定dp数组以及下标的含义
34+ ## 确定dp数组以及下标的含义
3435
3536对于背包问题,有一种写法, 是使用二维数组,即** dp[ i] [ j ] 表示从下标为[ 0-i] 的物品里任意取,放进容量为j的背包,价值总和最大是多少** 。
3637
@@ -40,7 +41,18 @@ leetcode上没有纯01背包的问题,都是需要转化为01背包的题目
4041
4142** 要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的** ,如果哪里看懵了,就来回顾一下i代表什么,j又代表什么。
4243
43- * dp数组如何初始化
44+ ##确定递推公式
45+
46+ 再回顾一下dp[ i] [ j ] 的含义:从下标为[ 0-i] 的物品里任意取,放进容量为j的背包,价值总和最大是多少。
47+
48+ 那么可以有两个方向推出来dp[ i] [ j ] ,
49+
50+ * 由dp[ i - 1] [ j ] 推出,即背包里不放物品i的最大价值,此时dp[ i] [ j ] 就是dp[ i - 1] [ j ]
51+ * 由dp[ i - 1] [ j - weight[ i]] 推出,dp[ i - 1] [ j - weight[ i]] 为背包容量为j - weight[ i] 的时候不放物品i的最大价值,那么dp[ i - 1] [ j - weight[ i]] + value[ i] (物品i的价值),就是背包放物品i得到的最大价值
52+
53+ 所以递归公式: dp[ i] [ j ] = max(dp[ i - 1] [ j ] , dp[ i - 1] [ j - weight[ i]] + value[ i] );
54+
55+ ##dp数组如何初始化
4456
4557** 关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱** 。
4658
@@ -65,18 +77,8 @@ dp[i][j]在推导的时候一定是取价值最大的数,如果题目给的价
6577
6678** 很明显,红框的位置就是我们要求的结果**
6779
68- * 确定递推公式
6980
70- 再回顾一下dp[ i] [ j ] 的含义:从下标为[ 0-i] 的物品里任意取,放进容量为j的背包,价值总和最大是多少。
71-
72- 那么可以有两个方向推出来dp[ i] [ j ] ,
73-
74- * 由dp[ i - 1] [ j ] 推出,即背包里不放物品i的最大价值,此时dp[ i] [ j ] 就是dp[ i - 1] [ j ]
75- * 由dp[ i - 1] [ j - weight[ i]] 推出,dp[ i - 1] [ j - weight[ i]] 为背包容量为j - weight[ i] 的时候不放物品i的最大价值,那么dp[ i - 1] [ j - weight[ i]] + value[ i] (物品i的价值),就是背包放物品i得到的最大价值
76-
77- 所以递归公式: dp[ i] [ j ] = max(dp[ i - 1] [ j ] , dp[ i - 1] [ j - weight[ i]] + value[ i] );
78-
79- * 确定遍历顺序
81+ ##确定遍历顺序
8082
8183确定递归公式之后,还要确定遍历顺序。
8284
@@ -118,18 +120,11 @@ for (int j = weight[0]; j <= bagWeight; j++) {
118120
119121** 所以一定要倒叙遍历,保证物品0只被放入一次!这一点对01背包很重要,后面在讲解滚动数组的时候,还会用到倒叙遍历来保证物品使用一次!**
120122
121- 初始化dp数组之后,就可以先遍历物品,在遍历背包,然后使用公式推导了,代码如下:
122123
123- ```
124- // 遍历过程
125- for(int i = 1; i < weight.size(); i++) { // 遍历物品
126- for(int j = 0; j <= bagWeight; j++) { // 遍历背包重量
127- if (j < weight[i]) dp[i][j] = dp[i - 1][j];
128- else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
124+ ##举例推导dp数组
125+
126+ dp[ ] [ ] = dp[ ] [ ] ** 应该这样手动推动一下**
129127
130- }
131- }
132- ```
133128来看一下对应的dp数组的数值,如图:
134129
135130<img src =' ../pics/动态规划-背包问题4.png ' width =600 > </img ></div >
@@ -144,6 +139,21 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
144139
145140主要就是自己没有动手推导一下dp数组的演变过程,如果推导明白了,代码写出来就算有问题,只要把dp数组打印出来,对比一下和自己推导的有什么差异,很快就可以发现问题了。
146141
142+ ##遍历过程代码
143+
144+ 初始化dp数组之后,就可以先遍历物品,在遍历背包,然后使用公式推导了,代码如下:
145+
146+ ```
147+ // 遍历过程
148+ for(int i = 1; i < weight.size(); i++) { // 遍历物品
149+ for(int j = 0; j <= bagWeight; j++) { // 遍历背包重量
150+ if (j < weight[i]) dp[i][j] = dp[i - 1][j];
151+ else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
152+
153+ }
154+ }
155+ ```
156+
147157遍历过程的代码其实优化的,我是为了把dp数组里数值完整表现出来,精简一下可以是:
148158
149159```
@@ -155,7 +165,7 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
155165}
156166```
157167
158- 完整测试代码:
168+ ## 完整代码
159169
160170``` C++
161171void 01bagProblem() {