Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5409045

Browse files
committed
Update 04.Knapsack-Problem-04.md
1 parent f138984 commit 5409045

File tree

1 file changed

+278
-2
lines changed

1 file changed

+278
-2
lines changed

‎Contents/10.Dynamic-Programming/04.Knapsack-Problem/04.Knapsack-Problem-04.md‎

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,289 @@
22

33
## 5. 分组背包问题
44

5-
> **分组背包问题**:有 $n$ 组物品和一个最多能装重量为 $W$ 的背包,第 $i$ 组物品的件数为 $count[i],ドル第 $i$ 组的第 $j$ 个物品重量为 $weight[i][j],ドル价值为 $value[i][j]$。每组物品中最多只能选择 1ドル$ 件物品装入背包。请问在总重量不超过背包载重上限的情况下,能装入背包的最大价值是多少?
5+
> **分组背包问题**:有 $n$ 组物品和一个最多能装重量为 $W$ 的背包,第 $i$ 组物品的件数为 $group\underline{}count[i],ドル第 $i$ 组的第 $j$ 个物品重量为 $weight[i][j],ドル价值为 $value[i][j]$。每组物品中最多只能选择 1ドル$ 件物品装入背包。请问在总重量不超过背包载重上限的情况下,能装入背包的最大价值是多少?
6+
7+
### 5.1 分组背包问题基本思路
8+
9+
#### 思路 1:动态规划 + 二维基本思路
10+
11+
###### 1. 划分阶段
12+
13+
按照物品种类的序号、当前背包的载重上限进行阶段划分。
14+
15+
###### 2. 定义状态
16+
17+
定义状态 $dp[i][w]$ 表示为:前 $i$ 组物品放入一个最多能装重量为 $w$ 的背包中,可以获得的最大价值。
18+
19+
状态 $dp[i][w]$ 是一个二维数组,其中第一维代表「当前正在考虑的物品组数」,第二维表示「当前背包的载重上限」,二维数组值表示「可以获得的最大价值」。
20+
21+
###### 3. 状态转移方程
22+
23+
由于我们可以不选择 $i - 1$ 组物品中的任何物品,也可以从第 $i - 1$ 组物品的第 0ドル \sim group\underline{}count[i - 1] - 1$ 件物品中随意选择 1ドル$ 件物品,所以状态 $dp[i][w]$ 可能从以下方案中选择最大值:
24+
25+
1. 不选择第 $i - 1$ 组中的任何物品:可以获得的最大价值为 $dp[i - 1][w]$。
26+
2. 选择第 $i - 1$ 组物品中第 0ドル$ 件:可以获得的最大价值为 $dp[i - 1][w - weight[i - 1][0]] + value[i - 1][0]$。
27+
3. 选择第 $i - 1$ 组物品中第 1ドル$ 件:可以获得的最大价值为 $dp[i - 1][w - weight[i - 1][1]] + value[i - 1][1]$。
28+
4. ......
29+
5. 选择第 $i - 1$ 组物品中最后 1ドル$ 件:假设 $k = group\underline{}count[i - 1] - 1,ドル则可以获得的最大价值为 $dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k]$。
30+
31+
则状态转移方程为:
32+
33+
$dp[i][w] = max \lbrace dp[i - 1][w],dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k] \rbrace , \quad 0 \le k \le group\underline{}count[i - 1]$
34+
35+
###### 4. 初始条件
36+
37+
- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。
38+
- 前 0ドル$ 组物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w] = 0$。
39+
40+
###### 5. 最终结果
41+
42+
根据我们之前定义的状态,$dp[i][w]$ 表示为:前 $i$ 组物品放入一个最多能装重量为 $w$ 的背包中,可以获得的最大价值。则最终结果为 $dp[size][W],ドル其中 $size$ 为物品的种类数,$W$ 为背包的载重上限。
43+
44+
#### 思路 1:代码
45+
46+
```Python
47+
class Solution:
48+
# 思路 1:动态规划 + 二维基本思路
49+
def groupPackMethod1(self, group_count: [int], weight: [[int]], value: [[int]], W: int):
50+
size = len(group_count)
51+
dp = [[0 for _ in range(W + 1)] for _ in range(size + 1)]
52+
53+
# 枚举前 i 组物品
54+
for i in range(1, size + 1):
55+
# 枚举背包装载重量
56+
for w in range(W + 1):
57+
# 枚举第 i - 1 组物品能取个数
58+
dp[i][w] = dp[i - 1][w]
59+
for k in range(group_count[i - 1]):
60+
if w >= weight[i - 1][k]:
61+
# dp[i][w] 取所有 dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k] 中最大值
62+
dp[i][w] = max(dp[i][w], dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k])
63+
```
64+
65+
#### 思路 1:复杂度分析
66+
67+
- **时间复杂度**:$O(n \times W \times C),ドル其中 $n$ 为物品分组数量,$W$ 为背包的载重上限,$C$ 是每组物品的数量。因为 $n \times C = \sum group\underline{}count[i],ドル所以时间复杂度也可以写成 $O(W \times \sum group\underline{}count[i])$。
68+
- **空间复杂度**:$O(n \times W)$。
69+
70+
### 5.2 分组背包问题滚动数组优化
71+
72+
#### 思路 2:动态规划 + 滚动数组优化
73+
74+
###### 1. 划分阶段
75+
76+
按照当前背包的载重上限进行阶段划分。
77+
78+
###### 2. 定义状态
79+
80+
定义状态 $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中,可以获得的最大价值。
81+
82+
###### 3. 状态转移方程
83+
84+
$dp[w] = max \lbrace dp[w], \quad dp[w - weight[i - 1][k]] + value[i - 1][k] \rbrace ,\quad 0 \le k \le group\underline{}count[i - 1]$
85+
86+
###### 4. 初始条件
87+
88+
- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。
89+
90+
###### 5. 最终结果
91+
92+
根据我们之前定义的状态, $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中,可以获得的最大价值。则最终结果为 $dp[W],ドル其中 $W$ 为背包的载重上限。
93+
94+
#### 思路 2:代码
95+
96+
```Python
97+
class Solution:
98+
# 思路 2:动态规划 + 滚动数组优化
99+
def groupPackMethod2(self, group_count: [int], weight: [[int]], value: [[int]], W: int):
100+
size = len(group_count)
101+
dp = [0 for _ in range(W + 1)]
102+
103+
# 枚举前 i 组物品
104+
for i in range(1, size + 1):
105+
# 逆序枚举背包装载重量
106+
for w in range(W, -1, -1):
107+
# 枚举第 i - 1 组物品能取个数
108+
for k in range(group_count[i - 1]):
109+
if w >= weight[i - 1][k]:
110+
# dp[w] 取所有 dp[w - weight[i - 1][k]] + value[i - 1][k] 中最大值
111+
dp[w] = max(dp[w], dp[w - weight[i - 1][k]] + value[i - 1][k])
112+
113+
return dp[W]
114+
```
115+
116+
#### 思路 2:复杂度分析
117+
118+
- **时间复杂度**:$O(n \times W \times C),ドル其中 $n$ 为物品分组数量,$W$ 为背包的载重上限,$C$ 是每组物品的数量。因为 $n \times C = \sum group\underline{}count[i],ドル所以时间复杂度也可以写成 $O(W \times \sum group\underline{}count[i])$。
119+
- **空间复杂度**:$O(W)$。
6120

7121
## 6. 二维费用背包问题
8122

9-
> **二维费用背包问题**:
123+
> **二维费用背包问题**:有 $n$ 件物品和有一个最多能装重量为 $W$、容量为 $V$ 的背包。第 $i$ 件物品的重量为 $weight[i],ドル体积为 $volume[i],ドル价值为 $value[i],ドル每件物品有且只有 1ドル$ 件。请问在总重量不超过背包载重上限、容量上限的情况下,能装入背包的最大价值是多少?
124+
125+
### 6.1 二维费用背包问题基本思路
126+
127+
我们可以参考「0-1 背包问题」的状态定义和基本思路,在「0-1 背包问题」基本思路的基础上,增加一个维度用于表示物品的容量。
128+
129+
#### 思路 1:动态规划 + 三维基本思路
130+
131+
###### 1. 划分阶段
132+
133+
按照物品种类的序号、当前背包的载重上限、容量上限进行阶段划分
134+
135+
###### 2. 定义状态
136+
137+
定义状态 $dp[i][w][v]$ 为:前 $i$ 件物品放入一个最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。
138+
139+
###### 3. 状态转移方程
140+
141+
$dp[i][w][v] = max(dp[i - 1][w][v], dp[i - 1][w - weight[i - 1]][v - volume[i - 1]] + value[i - 1]), \quad 0 \le weight[i - 1] \le w, 0 \le volume[i - 1] \le v$
142+
143+
> 注意:采用这种「状态定义」和「状态转移方程」,往往会导致内存超出要求限制,所以一般我们会采用「滚动数组」对算法的空间复杂度进行优化。
144+
145+
###### 4. 初始条件
146+
147+
- 如果背包载重上限为 0ドル$ 或者容量上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0][v] = 0,dp[i][w][0] = 0$。
148+
- 前 0ドル$ 组物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w][v] = 0$。
149+
150+
###### 5. 最终结果
151+
152+
根据我们之前定义的状态, $dp[i][w][v]$ 表示为:前 $i$ 件物品放入一个最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。则最终结果为 $dp[size][W][V],ドル其中 $size$ 为物品的种类数,$W$ 为背包的载重上限,$V$ 为背包的容量上限。
153+
154+
#### 思路 1:代码
155+
156+
```Python
157+
class Solution:
158+
# 思路 1:动态规划 + 三维基本思路
159+
def twoDCostPackMethod1(self, weight: [int], volume: [int], value: [int], W: int, V: int):
160+
size = len(weight)
161+
dp = [[[0 for _ in range(V + 1)] for _ in range(W + 1)] for _ in range(size + 1)]
162+
163+
# 枚举前 i 组物品
164+
for i in range(1, N + 1):
165+
# 枚举背包装载重量
166+
for w in range(W + 1):
167+
# 枚举背包装载容量
168+
for v in range(V + 1):
169+
# 第 i - 1 件物品装不下
170+
if w < weight[i - 1] or v < volume[i - 1]:
171+
# dp[i][w][v] 取「前 i - 1 件物品装入装载重量为 w、装载容量为 v 的背包中的最大价值」
172+
dp[i][w][v] = dp[i - 1][w][v]
173+
else:
174+
# dp[i][w][v] 取所有 dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] 中最大值
175+
dp[i][w][v] = max(dp[i - 1][w][v], dp[i - 1][w - weight[i - 1]][v - volume[i - 1]] + value[i - 1])
176+
177+
return dp[size][W][V]
178+
```
179+
180+
#### 思路 1:复杂度分析
181+
182+
- **时间复杂度**:$O(n \times W \times V),ドル其中 $n$ 为物品分组数量,$W$ 为背包的载重上限,$V$ 为背包的容量上限。
183+
- **空间复杂度**:$O(n \times W \times V)$。
184+
185+
### 6.2 二维费用背包问题滚动数组优化
186+
187+
#### 思路 2:动态规划 + 滚动数组优化
188+
189+
###### 1. 划分阶段
190+
191+
按照当前背包的载重上限、容量上限进行阶段划分。
192+
193+
###### 2. 定义状态
194+
195+
定义状态 $dp[w][v]$ 表示为:将物品装入最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。
196+
197+
###### 3. 状态转移方程
198+
199+
$dp[w][v] = max \lbrace dp[w][v], \quad dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] \rbrace , \quad 0 \le weight[i - 1] \le w, 0 \le volume[i - 1] \le v$
200+
201+
###### 4. 初始条件
202+
203+
- 如果背包载重上限为 0ドル$ 或者容量上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[w][0] = 0,dp[0][v] = 0$。
204+
205+
###### 5. 最终结果
206+
207+
根据我们之前定义的状态, $dp[w][v]$ 表示为:将物品装入最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。则最终结果为 $dp[W][V],ドル其中 $W$ 为背包的载重上限,$V$ 为背包的容量上限。
208+
209+
#### 思路 2:代码
210+
211+
```Python
212+
class Solution:
213+
# 思路 2:动态规划 + 滚动数组优化
214+
def twoDCostPackMethod2(self, weight: [int], volume: [int], value: [int], W: int, V: int):
215+
size = len(weight)
216+
dp = [[0 for _ in range(V + 1)] for _ in range(W + 1)]
217+
218+
# 枚举前 i 组物品
219+
for i in range(1, N + 1):
220+
# 逆序枚举背包装载重量
221+
for w in range(W, weight[i - 1] - 1, -1):
222+
# 逆序枚举背包装载容量
223+
for v in range(V, volume[i - 1] - 1, -1):
224+
# dp[w][v] 取所有 dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] 中最大值
225+
dp[w][v] = max(dp[w][v], dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1])
226+
227+
return dp[W][V]
228+
```
229+
230+
#### 思路 2:复杂度分析
231+
232+
- **时间复杂度**:$O(n \times W \times V),ドル其中 $n$ 为物品分组数量,$W$ 为背包的载重上限,$V$ 为背包的容量上限。
233+
- **空间复杂度**:$O(W \times V)$。
234+
235+
## 7. 背包问题变种
236+
237+
### 7.1 背包问题求方案总数
238+
239+
> **背包问题求方案数**:在给定背包重量 $W,ドル每件物品重量 $weight[i],ドル物品间相互关系(分组、依赖等)的背包问题中。求解出装满背包或将背包装至某一指定重量的方案总数。
240+
241+
这种问题就是将原有状态转移方程中的「求最大值」变为「求和」即可。我们以「完全背包问题」求解方案总数为例。
242+
243+
> **完全背包问题求解方案数**:有 $n$ 种物品和一个最多能装重量为 $W$ 的背包,第 $i$ 种物品的重量为 $weight[i],ドル价值为 $value[i],ドル每种物品数量没有限制。请问装满重量为 $W$ 的背包,一共有多少种方案。
244+
245+
如果使用二维状态定义,可定义状态 $dp[i][w]$ 为:前 $i$ 种物品放入一个最多能装重量为 $w$ 的背包中,一共有多少种方案。
246+
247+
则状态转移方程为:$dp[i][w] = sum(dp[i - 1][w], \quad dp[i][w - weight[i - 1]])$。
248+
249+
如果进行滚动数组优化中,使用一位状态定义,可定义状态 $dp[w]$ 为:装满重量为 $w$ 的背包的方案总数。则状态转移方程为:$dp[w] = sum(dp[w], \quad dp[w - weight[i - 1]])$。
250+
251+
###### 1. 划分阶段
252+
253+
按照物品种类的序号、当前背包的载重上限进行阶段划分。
254+
255+
###### 2. 定义状态
256+
257+
定义状态 $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中的方案总数。
258+
259+
###### 3. 状态转移方程
260+
261+
$dp[w] = sum \lbrace dp[w], \quad dp[w - weight[i - 1]] \rbrace $
262+
263+
###### 4. 初始条件
264+
265+
- 如果背包载重上限为 0ドル,ドル则一共有 1ドル$ 种方案(什么也不装),即 $dp[0] = 1$。
266+
267+
###### 5. 最终结果
268+
269+
根据我们之前定义的状态, $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中的方案总数。则最终结果为 $dp[W],ドル其中 $W$ 为背包的载重上限。
270+
271+
#### 思路 1:代码
272+
273+
```Python
274+
275+
```
276+
277+
#### 思路 1:复杂度分析
278+
279+
- **时间复杂度**:$O(n \times W),ドル其中 $n$ 为物品种类数量,$W$ 为背包的载重上限。
280+
- **空间复杂度**:$O(W)$。
281+
282+
### 7.2 背包问题求最优方案数
283+
284+
### 7.3 背包问题输出方案
10285

11286
## 参考资料
12287

13288
- 【资料】[背包九讲 - 崔添翼](https://github.com/tianyicui/pack)
14289
- 【文章】[背包 DP - OI Wiki](https://oi-wiki.org/dp/knapsack/)
290+
- 【文章】[【动态规划/背包问题】分组背包问题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247487504&idx=1&sn=9ac523ec0ac14c8634a229f8c3f919d7&chksm=fd9cbb0fcaeb32196b80a40e4408f6a7e2651167e0b9e31aa6d7c6109fbc2117340a59db12a1&scene=178&cur_album_id=1751702161341628417#rd)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /