1
1
2
2
leetcode上没有纯01背包的问题,都是需要转化为01背包的题目,所以我先把通过纯01背包问题,把01背包原理讲清楚,后序讲解leetcode题目的时候,重点就是如何转化为01背包问题了。
3
- ## 01 背包
3
+
4
+ # 01 背包
4
5
5
6
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[ i] ,得到的价值是value[ i] 。** 每件物品只能用一次** ,求解将哪些物品装入背包里物品价值总和最大。
6
7
@@ -30,7 +31,7 @@ leetcode上没有纯01背包的问题,都是需要转化为01背包的题目
30
31
31
32
以下讲解和图示中出现的数字都是以这个例子为例。
32
33
33
- * 确定dp数组以及下标的含义
34
+ ## 确定dp数组以及下标的含义
34
35
35
36
对于背包问题,有一种写法, 是使用二维数组,即** dp[ i] [ j ] 表示从下标为[ 0-i] 的物品里任意取,放进容量为j的背包,价值总和最大是多少** 。
36
37
@@ -40,7 +41,18 @@ leetcode上没有纯01背包的问题,都是需要转化为01背包的题目
40
41
41
42
** 要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的** ,如果哪里看懵了,就来回顾一下i代表什么,j又代表什么。
42
43
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数组如何初始化
44
56
45
57
** 关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱** 。
46
58
@@ -65,18 +77,8 @@ dp[i][j]在推导的时候一定是取价值最大的数,如果题目给的价
65
77
66
78
** 很明显,红框的位置就是我们要求的结果**
67
79
68
- * 确定递推公式
69
80
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
+ ## 确定遍历顺序
80
82
81
83
确定递归公式之后,还要确定遍历顺序。
82
84
@@ -118,18 +120,11 @@ for (int j = weight[0]; j <= bagWeight; j++) {
118
120
119
121
** 所以一定要倒叙遍历,保证物品0只被放入一次!这一点对01背包很重要,后面在讲解滚动数组的时候,还会用到倒叙遍历来保证物品使用一次!**
120
122
121
- 初始化dp数组之后,就可以先遍历物品,在遍历背包,然后使用公式推导了,代码如下:
122
123
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[ ] [ ] ** 应该这样手动推动一下**
129
127
130
- }
131
- }
132
- ```
133
128
来看一下对应的dp数组的数值,如图:
134
129
135
130
<img src =' ../pics/动态规划-背包问题4.png ' width =600 > </img ></div >
@@ -144,6 +139,21 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
144
139
145
140
主要就是自己没有动手推导一下dp数组的演变过程,如果推导明白了,代码写出来就算有问题,只要把dp数组打印出来,对比一下和自己推导的有什么差异,很快就可以发现问题了。
146
141
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
+
147
157
遍历过程的代码其实优化的,我是为了把dp数组里数值完整表现出来,精简一下可以是:
148
158
149
159
```
@@ -155,7 +165,7 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
155
165
}
156
166
```
157
167
158
- 完整测试代码:
168
+ ## 完整代码
159
169
160
170
``` C++
161
171
void 01bagProblem() {
0 commit comments