From c3bb633656497ae8086118e4e35125783ed8f69d Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月24日 15:11:29 +0800 Subject: [PATCH 01/13] Update 03.Knapsack-Problem-03.md --- .../04.Knapsack-Problem/03.Knapsack-Problem-03.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md index 0ea48d92..f5d29b6e 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md @@ -37,6 +37,7 @@ $dp[i][w] = max \lbrace dp[i - 1][w - k \times weight[i - 1]] + k \times value[i ```Python class Solution: + # 思路 1:动态规划 + 二维基本思路 def multiplePackMethod1(self, weight: [int], value: [int], count: [int], W: int): size = len(weight) dp = [[0 for _ in range(W + 1)] for _ in range(size + 1)] @@ -44,7 +45,7 @@ class Solution: # 枚举前 i 种物品 for i in range(1, size + 1): # 枚举背包装载重量 - for w in range(1, W + 1): + for w in range(W + 1): # 枚举第 i 种物品能取个数 for k in range(min(count[i - 1], w // weight[i - 1]) + 1): # dp[i][w] 取所有 dp[i - 1][w - k * weight[i - 1] + k * value[i - 1] 中最大值 @@ -55,7 +56,7 @@ class Solution: #### 思路 1:复杂度分析 -- **时间复杂度**:$O(n \times W \times \sum count[i]),ドル其中 $n$ 为物品种类数量,$W$ 为背包的载重上限,$count[i]$ 是第 $i$ 种物品的重量。 +- **时间复杂度**:$O(n \times W \times C),ドル其中 $n$ 为物品种类数量,$W$ 为背包的载重上限,$C$ 是物品的数量数组长度。因为 $n \times C = \sum count[i],ドル所以时间复杂度也可以写成 $O(W \times \sum count[i])$。 - **空间复杂度**:$O(n \times W)$。 ### 4.2 多重背包问题滚动数组优化 @@ -91,7 +92,8 @@ $dp[w] = max \lbrace dp[w - k \times weight[i - 1]] + k \times value[i - 1] \rbr #### 思路 2:代码 ```Python -class Solution: +class Solution: + # 思路 2:动态规划 + 滚动数组优化 def multiplePackMethod2(self, weight: [int], value: [int], count: [int], W: int): size = len(weight) dp = [0 for _ in range(W + 1)] @@ -167,9 +169,11 @@ $dp[w] = max \lbrace dp[w - weight \underline{ } new[i - 1]] + value \underline{ ```Python class Solution: + # 思路 3:动态规划 + 二进制优化 def multiplePackMethod3(self, weight: [int], value: [int], count: [int], W: int): weight_new, value_new = [], [] + # 二进制优化 for i in range(len(weight)): cnt = count[i] k = 1 From da44c7b5f3f58e699deb01672705e42b2c238aed Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 14:38:57 +0800 Subject: [PATCH 02/13] =?UTF-8?q?Update=200231.=202=20=E7=9A=84=E5=B9=82.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0231. 2 347円232円204円345円271円202円.md" | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git "a/Solutions/0231. 2 347円232円204円345円271円202円.md" "b/Solutions/0231. 2 347円232円204円345円271円202円.md" index 559dfe4a..ac7920f0 100644 --- "a/Solutions/0231. 2 347円232円204円345円271円202円.md" +++ "b/Solutions/0231. 2 347円232円204円345円271円202円.md" @@ -5,19 +5,66 @@ ## 题目大意 -给定一个整数,判断它是否是 2 的幂次方。 +**描述**:给定一个整数 $n$。 + +**要求**:判断该整数 $n$ 是否是 2ドル$ 的幂次方。如果是,返回 `True`;否则,返回 `False`。 + +**说明**: + +- $-2^{31} \le n \le 2^{31} - 1$ + +**示例**: + +- 示例 1: + +```Python +输入:n = 1 +输出:True +解释:2^0 = 1 +``` + +- 示例 2: + +```Python +输入:n = 16 +输出:True +解释:2^4 = 16 +``` ## 解题思路 -最简单的是循环。不断判断 n 是否能整除 2,若能整除,则让 n 整除 2,直到 n < 2。若最后 n == 1,则返回 True,否则则返回 False。 +### 思路 1:循环判断 -另一种方式是数论判断。 +1. 不断判断 $n$ 是否能整除 2ドル$。 + 1. 如果不能整除,则返回 `False`。 + 2. 如果能整除,则让 $n$ 整除 2ドル,ドル直到 $n < 2$。 +2. 如果最后 $n == 1,ドル则返回 `True`,否则则返回 `False`。 -因为 n 的最大值为 2ドル^{31}-1$。计算出在 n 的范围内,2 的幂次方最大为 3ドル^{30} = 1073741824$。 +### 思路 1:代码 -2 为质数,则 2ドル^{30}$ 的除数只有 2ドル^0, 2^1, ..., 2^{30}$。所以若 n 为 2 的幂次方,则 n 肯定能被 2ドル^{30}$ 整除,直接判断即可。 +```Python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n <= 0: + return False + + while n % 2 == 0: + n //= 2 + return n == 1 +``` + +### 思路 1:复杂度分析 -## 代码 +- **时间复杂度**:$O(\log_2 n)$。 +- **空间复杂度**:$O(1)$。 + +### 思路 2:数论判断 + +因为 $n$ 能取的最大值为 2ドル^{31}-1$。我们可以计算出:在 $n$ 的范围内,2ドル$ 的幂次方最大为 2ドル^{30} = 1073741824$。 + +因为 2ドル$ 为质数,则 2ドル^{30}$ 的除数只有 2ドル^0, 2^1, ..., 2^{30}$。所以如果 $n$ 为 2ドル$ 的幂次方,则 $n$ 肯定能被 2ドル^{30}$ 整除,直接判断即可。 + +### 思路 2:代码 ```Python class Solution: @@ -25,3 +72,8 @@ class Solution: return n> 0 and 1073741824 % n == 0 ``` +### 思路 2:复杂度分析 + +- **时间复杂度**:$O(1)$。 +- **空间复杂度**:$O(1)$。 + From 3effbca39a7bd3198daddac24627cfe1e87cc8fe Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 14:39:15 +0800 Subject: [PATCH 03/13] Update Pack-CompletePack.py --- Templates/10.Dynamic-Programming/Pack-CompletePack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/10.Dynamic-Programming/Pack-CompletePack.py b/Templates/10.Dynamic-Programming/Pack-CompletePack.py index 2d17b078..6fb0c942 100644 --- a/Templates/10.Dynamic-Programming/Pack-CompletePack.py +++ b/Templates/10.Dynamic-Programming/Pack-CompletePack.py @@ -8,7 +8,7 @@ def completePackMethod1(self, weight: [int], value: [int], W: int): for i in range(1, size + 1): # 枚举背包装载重量 for w in range(W + 1): - # 枚举第 i 种物品能取个数 + # 枚举第 i - 1 种物品能取个数 for k in range(w // weight[i - 1] + 1): # dp[i][w] 取所有 dp[i - 1][w - k * weight[i - 1] + k * value[i - 1] 中最大值 dp[i][w] = max(dp[i][w], dp[i - 1][w - k * weight[i - 1]] + k * value[i - 1]) From e8d7dc1e4d607021fb4bbe2cbe5487bd055aa873 Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 14:39:18 +0800 Subject: [PATCH 04/13] Update Pack-MultiplePack.py --- Templates/10.Dynamic-Programming/Pack-MultiplePack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Templates/10.Dynamic-Programming/Pack-MultiplePack.py b/Templates/10.Dynamic-Programming/Pack-MultiplePack.py index f9ff5553..e7a42c96 100644 --- a/Templates/10.Dynamic-Programming/Pack-MultiplePack.py +++ b/Templates/10.Dynamic-Programming/Pack-MultiplePack.py @@ -8,7 +8,7 @@ def multiplePackMethod1(self, weight: [int], value: [int], count: [int], W: int) for i in range(1, size + 1): # 枚举背包装载重量 for w in range(W + 1): - # 枚举第 i 种物品能取个数 + # 枚举第 i - 1 种物品能取个数 for k in range(min(count[i - 1], w // weight[i - 1]) + 1): # dp[i][w] 取所有 dp[i - 1][w - k * weight[i - 1] + k * value[i - 1] 中最大值 dp[i][w] = max(dp[i][w], dp[i - 1][w - k * weight[i - 1]] + k * value[i - 1]) @@ -24,7 +24,7 @@ def multiplePackMethod2(self, weight: [int], value: [int], count: [int], W: int) for i in range(1, size + 1): # 逆序枚举背包装载重量(避免状态值错误) for w in range(W, weight[i - 1] - 1, -1): - # 枚举第 i 种物品能取个数 + # 枚举第 i - 1 种物品能取个数 for k in range(min(count[i - 1], w // weight[i - 1]) + 1): # dp[w] 取所有 dp[w - k * weight[i - 1]] + k * value[i - 1] 中最大值 dp[w] = max(dp[w], dp[w - k * weight[i - 1]] + k * value[i - 1]) From 45549b5afc6a0cbb85feceee54df81b5f104e548 Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 14:39:21 +0800 Subject: [PATCH 05/13] Update Pack-GroupPack.py --- Templates/10.Dynamic-Programming/Pack-GroupPack.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Templates/10.Dynamic-Programming/Pack-GroupPack.py b/Templates/10.Dynamic-Programming/Pack-GroupPack.py index 176d9279..398dea64 100644 --- a/Templates/10.Dynamic-Programming/Pack-GroupPack.py +++ b/Templates/10.Dynamic-Programming/Pack-GroupPack.py @@ -8,7 +8,7 @@ def groupPackMethod1(self, group_count: [int], weight: [[int]], value: [[int]], for i in range(1, size + 1): # 枚举背包装载重量 for w in range(W + 1): - # 枚举第 i 组物品能取个数 + # 枚举第 i - 1 组物品能取个数 dp[i][w] = dp[i - 1][w] for k in range(group_count[i - 1]): if w>= weight[i - 1][k]: @@ -26,10 +26,10 @@ def groupPackMethod2(self, group_count: [int], weight: [[int]], value: [[int]], for i in range(1, size + 1): # 逆序枚举背包装载重量 for w in range(W, -1, -1): - # 枚举第 i 组物品能取个数 + # 枚举第 i - 1 组物品能取个数 for k in range(group_count[i - 1]): if w>= weight[i - 1][k]: - # dp[i][w] 取所有 dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k] 中最大值 + # dp[w] 取所有 dp[w - weight[i - 1][k]] + value[i - 1][k] 中最大值 dp[w] = max(dp[w], dp[w - weight[i - 1][k]] + value[i - 1][k]) return dp[W] From 35b5c04a0b1f3b27e9d7d3ae082806c572bb0b55 Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 14:39:29 +0800 Subject: [PATCH 06/13] Update 02.Knapsack-Problem-02.md --- .../04.Knapsack-Problem/02.Knapsack-Problem-02.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md index 5dac7158..01b5195b 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md @@ -60,7 +60,7 @@ class Solution: for i in range(1, size + 1): # 枚举背包装载重量 for w in range(W + 1): - # 枚举第 i 种物品能取个数 + # 枚举第 i - 1 种物品能取个数 for k in range(w // weight[i - 1] + 1): # dp[i][w] 取所有 dp[i - 1][w - k * weight[i - 1] + k * value[i - 1] 中最大值 dp[i][w] = max(dp[i][w], dp[i - 1][w - k * weight[i - 1]] + k * value[i - 1]) From 020e2a10a011b671f12c7ba90082bd8990b94825 Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 14:39:33 +0800 Subject: [PATCH 07/13] Update 03.Knapsack-Problem-03.md --- .../04.Knapsack-Problem/03.Knapsack-Problem-03.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md index f5d29b6e..ac8d029b 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md @@ -46,7 +46,7 @@ class Solution: for i in range(1, size + 1): # 枚举背包装载重量 for w in range(W + 1): - # 枚举第 i 种物品能取个数 + # 枚举第 i - 1 种物品能取个数 for k in range(min(count[i - 1], w // weight[i - 1]) + 1): # dp[i][w] 取所有 dp[i - 1][w - k * weight[i - 1] + k * value[i - 1] 中最大值 dp[i][w] = max(dp[i][w], dp[i - 1][w - k * weight[i - 1]] + k * value[i - 1]) @@ -102,7 +102,7 @@ class Solution: for i in range(1, size + 1): # 逆序枚举背包装载重量(避免状态值错误) for w in range(W, weight[i - 1] - 1, -1): - # 枚举第 i 种物品能取个数 + # 枚举第 i - 1 种物品能取个数 for k in range(min(count[i - 1], w // weight[i - 1]) + 1): # dp[w] 取所有 dp[w - k * weight[i - 1]] + k * value[i - 1] 中最大值 dp[w] = max(dp[w], dp[w - k * weight[i - 1]] + k * value[i - 1]) From ef99688136a016316fc3d4aa37e0c96234b3c04a Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 18:08:26 +0800 Subject: [PATCH 08/13] Update 01.Knapsack-Problem-01.md --- .../04.Knapsack-Problem/01.Knapsack-Problem-01.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/01.Knapsack-Problem-01.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/01.Knapsack-Problem-01.md index ebc8d349..d539bfe3 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/01.Knapsack-Problem-01.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/01.Knapsack-Problem-01.md @@ -56,7 +56,7 @@ $dp[i][w] = \begin{cases} dp[i - 1][w] & w < weight[i - 1] \cr max \lbrace dp[i ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 - 前 0ドル$ 件物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w] = 0$。 ###### 5. 最终结果 @@ -128,7 +128,7 @@ $dp[w] = \begin{cases} dp[w] & w < weight[i - 1] \cr max \lbrace dp[w], dp[w - w ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 ###### 5. 最终结果 @@ -222,7 +222,7 @@ $dp[w] = \begin{cases} dp[w] & w < nums[i - 1] \cr max \lbrace dp[w], \quad dp[w ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么元素,可以获得的元素和一定是 0ドル,ドル即 $dp[0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么元素,可以获得的元素和一定是 0ドル,ドル即 $dp[0] = 0$。 ###### 5. 最终结果 From 9d856608f41efae1287f3c5099ce8e174c2ccd6f Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 18:08:31 +0800 Subject: [PATCH 09/13] Update 02.Knapsack-Problem-02.md --- .../04.Knapsack-Problem/02.Knapsack-Problem-02.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md index 01b5195b..2ef7120c 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/02.Knapsack-Problem-02.md @@ -40,7 +40,7 @@ $dp[i][w] = max \lbrace dp[i - 1][w - k \times weight[i - 1]] + k \times value[i ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 - 前 0ドル$ 种物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w] = 0$。 ###### 5. 最终结果 @@ -133,7 +133,7 @@ $\quad dp[i][w] = \begin{cases} dp[i - 1][w] & w < weight[i - 1] \cr max \lbrac ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 - 前 0ドル$ 种物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w] = 0$。 ###### 5. 最终结果 @@ -201,7 +201,7 @@ $dp[w] = \begin{cases} dp[w] & w < weight[i - 1] \cr max \lbrace dp[w], \quad d ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 ###### 5. 最终结果 From f138984d05e5a6680894c4418916cfded86d9bac Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 18:08:36 +0800 Subject: [PATCH 10/13] Update 03.Knapsack-Problem-03.md --- .../04.Knapsack-Problem/03.Knapsack-Problem-03.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md index ac8d029b..8d235cfa 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/03.Knapsack-Problem-03.md @@ -26,7 +26,7 @@ $dp[i][w] = max \lbrace dp[i - 1][w - k \times weight[i - 1]] + k \times value[i ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 - 前 0ドル$ 种物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w] = 0$。 ###### 5. 最终结果 @@ -83,7 +83,7 @@ $dp[w] = max \lbrace dp[w - k \times weight[i - 1]] + k \times value[i - 1] \rbr ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 ###### 5. 最终结果 @@ -159,7 +159,7 @@ $dp[w] = max \lbrace dp[w - weight \underline{ } new[i - 1]] + value \underline{ ###### 4. 初始条件 -- 如果背包容量为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 ###### 5. 最终结果 From 5409045ead7536abcd797e50ebdef1731988b666 Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 18:09:09 +0800 Subject: [PATCH 11/13] Update 04.Knapsack-Problem-04.md --- .../04.Knapsack-Problem-04.md | 280 +++++++++++++++++- 1 file changed, 278 insertions(+), 2 deletions(-) diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/04.Knapsack-Problem-04.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/04.Knapsack-Problem-04.md index 39549d7b..59d8010c 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/04.Knapsack-Problem-04.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/04.Knapsack-Problem-04.md @@ -2,13 +2,289 @@ ## 5. 分组背包问题 -> **分组背包问题**:有 $n$ 组物品和一个最多能装重量为 $W$ 的背包,第 $i$ 组物品的件数为 $count[i],ドル第 $i$ 组的第 $j$ 个物品重量为 $weight[i][j],ドル价值为 $value[i][j]$。每组物品中最多只能选择 1ドル$ 件物品装入背包。请问在总重量不超过背包载重上限的情况下,能装入背包的最大价值是多少? +> **分组背包问题**:有 $n$ 组物品和一个最多能装重量为 $W$ 的背包,第 $i$ 组物品的件数为 $group\underline{}count[i],ドル第 $i$ 组的第 $j$ 个物品重量为 $weight[i][j],ドル价值为 $value[i][j]$。每组物品中最多只能选择 1ドル$ 件物品装入背包。请问在总重量不超过背包载重上限的情况下,能装入背包的最大价值是多少? + +### 5.1 分组背包问题基本思路 + +#### 思路 1:动态规划 + 二维基本思路 + +###### 1. 划分阶段 + +按照物品种类的序号、当前背包的载重上限进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[i][w]$ 表示为:前 $i$ 组物品放入一个最多能装重量为 $w$ 的背包中,可以获得的最大价值。 + +状态 $dp[i][w]$ 是一个二维数组,其中第一维代表「当前正在考虑的物品组数」,第二维表示「当前背包的载重上限」,二维数组值表示「可以获得的最大价值」。 + +###### 3. 状态转移方程 + +由于我们可以不选择 $i - 1$ 组物品中的任何物品,也可以从第 $i - 1$ 组物品的第 0ドル \sim group\underline{}count[i - 1] - 1$ 件物品中随意选择 1ドル$ 件物品,所以状态 $dp[i][w]$ 可能从以下方案中选择最大值: + +1. 不选择第 $i - 1$ 组中的任何物品:可以获得的最大价值为 $dp[i - 1][w]$。 +2. 选择第 $i - 1$ 组物品中第 0ドル$ 件:可以获得的最大价值为 $dp[i - 1][w - weight[i - 1][0]] + value[i - 1][0]$。 +3. 选择第 $i - 1$ 组物品中第 1ドル$ 件:可以获得的最大价值为 $dp[i - 1][w - weight[i - 1][1]] + value[i - 1][1]$。 +4. ...... +5. 选择第 $i - 1$ 组物品中最后 1ドル$ 件:假设 $k = group\underline{}count[i - 1] - 1,ドル则可以获得的最大价值为 $dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k]$。 + +则状态转移方程为: + +$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]$ + +###### 4. 初始条件 + +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0] = 0$。 +- 前 0ドル$ 组物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w] = 0$。 + +###### 5. 最终结果 + +根据我们之前定义的状态,$dp[i][w]$ 表示为:前 $i$ 组物品放入一个最多能装重量为 $w$ 的背包中,可以获得的最大价值。则最终结果为 $dp[size][W],ドル其中 $size$ 为物品的种类数,$W$ 为背包的载重上限。 + +#### 思路 1:代码 + +```Python +class Solution: + # 思路 1:动态规划 + 二维基本思路 + def groupPackMethod1(self, group_count: [int], weight: [[int]], value: [[int]], W: int): + size = len(group_count) + dp = [[0 for _ in range(W + 1)] for _ in range(size + 1)] + + # 枚举前 i 组物品 + for i in range(1, size + 1): + # 枚举背包装载重量 + for w in range(W + 1): + # 枚举第 i - 1 组物品能取个数 + dp[i][w] = dp[i - 1][w] + for k in range(group_count[i - 1]): + if w>= weight[i - 1][k]: + # dp[i][w] 取所有 dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k] 中最大值 + dp[i][w] = max(dp[i][w], dp[i - 1][w - weight[i - 1][k]] + value[i - 1][k]) +``` + +#### 思路 1:复杂度分析 + +- **时间复杂度**:$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])$。 +- **空间复杂度**:$O(n \times W)$。 + +### 5.2 分组背包问题滚动数组优化 + +#### 思路 2:动态规划 + 滚动数组优化 + +###### 1. 划分阶段 + +按照当前背包的载重上限进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中,可以获得的最大价值。 + +###### 3. 状态转移方程 + +$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]$ + +###### 4. 初始条件 + +- 如果背包载重上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[0] = 0$。 + +###### 5. 最终结果 + +根据我们之前定义的状态, $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中,可以获得的最大价值。则最终结果为 $dp[W],ドル其中 $W$ 为背包的载重上限。 + +#### 思路 2:代码 + +```Python +class Solution: + # 思路 2:动态规划 + 滚动数组优化 + def groupPackMethod2(self, group_count: [int], weight: [[int]], value: [[int]], W: int): + size = len(group_count) + dp = [0 for _ in range(W + 1)] + + # 枚举前 i 组物品 + for i in range(1, size + 1): + # 逆序枚举背包装载重量 + for w in range(W, -1, -1): + # 枚举第 i - 1 组物品能取个数 + for k in range(group_count[i - 1]): + if w>= weight[i - 1][k]: + # dp[w] 取所有 dp[w - weight[i - 1][k]] + value[i - 1][k] 中最大值 + dp[w] = max(dp[w], dp[w - weight[i - 1][k]] + value[i - 1][k]) + + return dp[W] +``` + +#### 思路 2:复杂度分析 + +- **时间复杂度**:$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])$。 +- **空间复杂度**:$O(W)$。 ## 6. 二维费用背包问题 -> **二维费用背包问题**: +> **二维费用背包问题**:有 $n$ 件物品和有一个最多能装重量为 $W$、容量为 $V$ 的背包。第 $i$ 件物品的重量为 $weight[i],ドル体积为 $volume[i],ドル价值为 $value[i],ドル每件物品有且只有 1ドル$ 件。请问在总重量不超过背包载重上限、容量上限的情况下,能装入背包的最大价值是多少? + +### 6.1 二维费用背包问题基本思路 + +我们可以参考「0-1 背包问题」的状态定义和基本思路,在「0-1 背包问题」基本思路的基础上,增加一个维度用于表示物品的容量。 + +#### 思路 1:动态规划 + 三维基本思路 + +###### 1. 划分阶段 + +按照物品种类的序号、当前背包的载重上限、容量上限进行阶段划分 + +###### 2. 定义状态 + +定义状态 $dp[i][w][v]$ 为:前 $i$ 件物品放入一个最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。 + +###### 3. 状态转移方程 + +$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$ + +> 注意:采用这种「状态定义」和「状态转移方程」,往往会导致内存超出要求限制,所以一般我们会采用「滚动数组」对算法的空间复杂度进行优化。 + +###### 4. 初始条件 + +- 如果背包载重上限为 0ドル$ 或者容量上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[i][0][v] = 0,dp[i][w][0] = 0$。 +- 前 0ドル$ 组物品所能获得的最大价值一定为 0ドル,ドル即 $dp[0][w][v] = 0$。 + +###### 5. 最终结果 + +根据我们之前定义的状态, $dp[i][w][v]$ 表示为:前 $i$ 件物品放入一个最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。则最终结果为 $dp[size][W][V],ドル其中 $size$ 为物品的种类数,$W$ 为背包的载重上限,$V$ 为背包的容量上限。 + +#### 思路 1:代码 + +```Python +class Solution: + # 思路 1:动态规划 + 三维基本思路 + def twoDCostPackMethod1(self, weight: [int], volume: [int], value: [int], W: int, V: int): + size = len(weight) + dp = [[[0 for _ in range(V + 1)] for _ in range(W + 1)] for _ in range(size + 1)] + + # 枚举前 i 组物品 + for i in range(1, N + 1): + # 枚举背包装载重量 + for w in range(W + 1): + # 枚举背包装载容量 + for v in range(V + 1): + # 第 i - 1 件物品装不下 + if w < weight[i - 1] or v < volume[i - 1]: + # dp[i][w][v] 取「前 i - 1 件物品装入装载重量为 w、装载容量为 v 的背包中的最大价值」 + dp[i][w][v] = dp[i - 1][w][v] + else: + # dp[i][w][v] 取所有 dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] 中最大值 + dp[i][w][v] = max(dp[i - 1][w][v], dp[i - 1][w - weight[i - 1]][v - volume[i - 1]] + value[i - 1]) + + return dp[size][W][V] +``` + +#### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n \times W \times V),ドル其中 $n$ 为物品分组数量,$W$ 为背包的载重上限,$V$ 为背包的容量上限。 +- **空间复杂度**:$O(n \times W \times V)$。 + +### 6.2 二维费用背包问题滚动数组优化 + +#### 思路 2:动态规划 + 滚动数组优化 + +###### 1. 划分阶段 + +按照当前背包的载重上限、容量上限进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[w][v]$ 表示为:将物品装入最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。 + +###### 3. 状态转移方程 + +$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$ + +###### 4. 初始条件 + +- 如果背包载重上限为 0ドル$ 或者容量上限为 0ドル,ドル则无论选取什么物品,可以获得的最大价值一定是 0ドル,ドル即 $dp[w][0] = 0,dp[0][v] = 0$。 + +###### 5. 最终结果 + +根据我们之前定义的状态, $dp[w][v]$ 表示为:将物品装入最多能装重量为 $w$、容量为 $v$ 的背包中,可以获得的最大价值。则最终结果为 $dp[W][V],ドル其中 $W$ 为背包的载重上限,$V$ 为背包的容量上限。 + +#### 思路 2:代码 + +```Python +class Solution: + # 思路 2:动态规划 + 滚动数组优化 + def twoDCostPackMethod2(self, weight: [int], volume: [int], value: [int], W: int, V: int): + size = len(weight) + dp = [[0 for _ in range(V + 1)] for _ in range(W + 1)] + + # 枚举前 i 组物品 + for i in range(1, N + 1): + # 逆序枚举背包装载重量 + for w in range(W, weight[i - 1] - 1, -1): + # 逆序枚举背包装载容量 + for v in range(V, volume[i - 1] - 1, -1): + # dp[w][v] 取所有 dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] 中最大值 + dp[w][v] = max(dp[w][v], dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1]) + + return dp[W][V] +``` + +#### 思路 2:复杂度分析 + +- **时间复杂度**:$O(n \times W \times V),ドル其中 $n$ 为物品分组数量,$W$ 为背包的载重上限,$V$ 为背包的容量上限。 +- **空间复杂度**:$O(W \times V)$。 + +## 7. 背包问题变种 + +### 7.1 背包问题求方案总数 + +> **背包问题求方案数**:在给定背包重量 $W,ドル每件物品重量 $weight[i],ドル物品间相互关系(分组、依赖等)的背包问题中。求解出装满背包或将背包装至某一指定重量的方案总数。 + +这种问题就是将原有状态转移方程中的「求最大值」变为「求和」即可。我们以「完全背包问题」求解方案总数为例。 + +> **完全背包问题求解方案数**:有 $n$ 种物品和一个最多能装重量为 $W$ 的背包,第 $i$ 种物品的重量为 $weight[i],ドル价值为 $value[i],ドル每种物品数量没有限制。请问装满重量为 $W$ 的背包,一共有多少种方案。 + +如果使用二维状态定义,可定义状态 $dp[i][w]$ 为:前 $i$ 种物品放入一个最多能装重量为 $w$ 的背包中,一共有多少种方案。 + +则状态转移方程为:$dp[i][w] = sum(dp[i - 1][w], \quad dp[i][w - weight[i - 1]])$。 + +如果进行滚动数组优化中,使用一位状态定义,可定义状态 $dp[w]$ 为:装满重量为 $w$ 的背包的方案总数。则状态转移方程为:$dp[w] = sum(dp[w], \quad dp[w - weight[i - 1]])$。 + +###### 1. 划分阶段 + +按照物品种类的序号、当前背包的载重上限进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中的方案总数。 + +###### 3. 状态转移方程 + +$dp[w] = sum \lbrace dp[w], \quad dp[w - weight[i - 1]] \rbrace $ + +###### 4. 初始条件 + +- 如果背包载重上限为 0ドル,ドル则一共有 1ドル$ 种方案(什么也不装),即 $dp[0] = 1$。 + +###### 5. 最终结果 + +根据我们之前定义的状态, $dp[w]$ 表示为:将物品装入最多能装重量为 $w$ 的背包中的方案总数。则最终结果为 $dp[W],ドル其中 $W$ 为背包的载重上限。 + +#### 思路 1:代码 + +```Python + +``` + +#### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n \times W),ドル其中 $n$ 为物品种类数量,$W$ 为背包的载重上限。 +- **空间复杂度**:$O(W)$。 + +### 7.2 背包问题求最优方案数 + +### 7.3 背包问题输出方案 ## 参考资料 - 【资料】[背包九讲 - 崔添翼](https://github.com/tianyicui/pack) - 【文章】[背包 DP - OI Wiki](https://oi-wiki.org/dp/knapsack/) +- 【文章】[【动态规划/背包问题】分组背包问题](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247487504&idx=1&sn=9ac523ec0ac14c8634a229f8c3f919d7&chksm=fd9cbb0fcaeb32196b80a40e4408f6a7e2651167e0b9e31aa6d7c6109fbc2117340a59db12a1&scene=178&cur_album_id=1751702161341628417#rd) From 97f6a4189ebf59bd423240ffe1ab5413372c15ed Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 18:09:17 +0800 Subject: [PATCH 12/13] Create Pack-2DCostPack.py --- .../10.Dynamic-Programming/Pack-2DCostPack.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Templates/10.Dynamic-Programming/Pack-2DCostPack.py diff --git a/Templates/10.Dynamic-Programming/Pack-2DCostPack.py b/Templates/10.Dynamic-Programming/Pack-2DCostPack.py new file mode 100644 index 00000000..7f6fa98a --- /dev/null +++ b/Templates/10.Dynamic-Programming/Pack-2DCostPack.py @@ -0,0 +1,37 @@ +class Solution: + # 思路 1:动态规划 + 三维基本思路 + def twoDCostPackMethod1(self, weight: [int], volume: [int], value: [int], W: int, V: int): + size = len(weight) + dp = [[[0 for _ in range(V + 1)] for _ in range(W + 1)] for _ in range(size + 1)] + + # 枚举前 i 组物品 + for i in range(1, N + 1): + # 枚举背包装载重量 + for w in range(W + 1): + # 枚举背包装载容量 + for v in range(V + 1): + # 第 i - 1 件物品装不下 + if w < weight[i - 1] or v < volume[i - 1]: + # dp[i][w][v] 取「前 i - 1 件物品装入装载重量为 w、装载容量为 v 的背包中的最大价值」 + dp[i][w][v] = dp[i - 1][w][v] + else: + # dp[i][w][v] 取所有 dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] 中最大值 + dp[i][w][v] = max(dp[i - 1][w][v], dp[i - 1][w - weight[i - 1]][v - volume[i - 1]] + value[i - 1]) + + return dp[size][W][V] + + # 思路 2:动态规划 + 滚动数组优化 + def twoDCostPackMethod2(self, weight: [int], volume: [int], value: [int], W: int, V: int): + size = len(weight) + dp = [[0 for _ in range(V + 1)] for _ in range(W + 1)] + + # 枚举前 i 组物品 + for i in range(1, N + 1): + # 逆序枚举背包装载重量 + for w in range(W, weight[i - 1] - 1, -1): + # 逆序枚举背包装载容量 + for v in range(V, volume[i - 1] - 1, -1): + # dp[w][v] 取所有 dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1] 中最大值 + dp[w][v] = max(dp[w][v], dp[w - weight[i - 1]][v - volume[i - 1]] + value[i - 1]) + + return dp[W][V] \ No newline at end of file From 6debcdd2fd541b99365e138fb5c550fbf8a5cbd4 Mon Sep 17 00:00:00 2001 From: ITCharge Date: 2023年3月27日 18:10:51 +0800 Subject: [PATCH 13/13] Create Pack-ProblemVariants.py --- .../10.Dynamic-Programming/Pack-ProblemVariants.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Templates/10.Dynamic-Programming/Pack-ProblemVariants.py diff --git a/Templates/10.Dynamic-Programming/Pack-ProblemVariants.py b/Templates/10.Dynamic-Programming/Pack-ProblemVariants.py new file mode 100644 index 00000000..b34153f1 --- /dev/null +++ b/Templates/10.Dynamic-Programming/Pack-ProblemVariants.py @@ -0,0 +1,13 @@ +class Solution: + # 完全问题求解方案总数 + def zeroOnePackNumbers(self, weight: [int], value: [int], W: int): + size = len(weight) + dp = [0 for _ in range(W + 1)] + + # 枚举前 i 种物品 + for i in range(1, size + 1): + # 正序枚举背包装载重量 + for w in range(weight[i - 1], W + 1): + dp[w] = sum(dp[w], dp[w - weight[i - 1]]) + + return dp[W] \ No newline at end of file

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