From b2dda568e246541a982cbf32a2af354f959af9ef Mon Sep 17 00:00:00 2001 From: Sainnhe Park Date: Thu, 9 Feb 2023 17:48:46 +0800 Subject: [PATCH] Update knapsack --- notes/src/knapsack.md | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/notes/src/knapsack.md b/notes/src/knapsack.md index 3ffcf02..0a49447 100644 --- a/notes/src/knapsack.md +++ b/notes/src/knapsack.md @@ -201,3 +201,96 @@ void complete_knapsack_problem_1d() { - 一种特殊情况是 `dp[0]`,它表示组合成总金额为 `0` 的组合数,我们必须把它赋值为 `1`,这是因为当我们执行 `dp[j] += dp[j - coins[i]]` 的时候,如果 `j == coins[i]` 那么显然应该自增 `1`。 - 看看能不能不初始化第一层,我们直接删掉代码试试发现可以,那就不初始化第一层。 - 遍历顺序都是从前向后 + +### [377. 组合总和 Ⅳ](https://leetcode.cn/problems/combination-sum-iv/) + +这道题我们要先分清楚组合和排列。 + +`(1, 2)` 和 `(2, 1)` 是不同的排列,同样的组合。 + +本题求的是排列数。 + +**如果求组合数就是外层 for 循环遍历物品,内层 for 遍历背包;** + +**如果求排列数就是外层 for 遍历背包,内层 for 循环遍历物品。** + +- `dp[i]` 为从 0 ~ j 中选取排列,凑成 `i` 的排列数 +- `dp[i] += dp[i - nums[j]]` +- `dp[0] = 1` 其它为 `0` +- 都从前向后 + +### [70. 爬楼梯](https://leetcode.cn/problems/climbing-stairs/) + +本题求的是排列数,因此外循环遍历背包容量,内循环遍历物品。 + +- `dp[i]` 为爬到第 `i` 阶的排列个数 +- `dp[i] += dp[i - nums[j]]` +- `dp[0] = 1` 其它为 `0` +- 都是从前往后 + +### [322. 零钱兑换](https://leetcode.cn/problems/coin-change/) + +本题求的是组合数,因此外循环遍历物品,内循环遍历背包容量。 + +- `dp[j]` 为凑成金额 `j` 所需的最少硬币数 +- `dp[j] = min{dp[j - coins[i]] + 1, dp[j]}` +- `dp[0] = 0` 其它为 `INT_MAX` +- 都是从前往后 + +### [279.完全平方数](https://leetcode.cn/problems/perfect-squares/) + +本题求的是组合数,因此外循环遍历物品,内循环遍历背包容量。 + +- `dp[j]` 为能凑成 `j` 的最少完全平方数的个数 +- `dp[j] = min{dp[j - i * i] + 1, dp[j]}` +- `dp[0] = 0` 其它为 `INT_MAX` +- 都是从前往后 + +## 多重背包 + +和 01 背包的区别在于,01 背包的每个元素只能用一次,而多重背包的每个物品能用 `ki` 次。 + +解决方法也很简单,把多重背包展开: + +| | Weight | Value | Numbers | +| :----: | :----: | :---: | :-----: | +| Item 0 | 1 | 15 | 2 | +| Item 1 | 3 | 20 | 3 | +| Item 2 | 4 | 30 | 2 | + +可以展开成这样的 01 背包: + +| | Weight | Value | +| :----: | :----: | :---: | +| Item 0 | 1 | 15 | +| Item 1 | 1 | 15 | +| Item 2 | 3 | 20 | +| Item 3 | 3 | 20 | +| Item 4 | 3 | 20 | +| Item 5 | 4 | 30 | +| Item 6 | 4 | 30 | + +```cpp +void multi_knapsack_2d() { + vector weight = {1, 3, 4}; + vector value = {15, 20, 30}; + vector nums = {2, 3, 2}; + int knapsackWeight = 10; + + // 展开 + for (int i = 0; i < nums.size(); i++) { + while (nums[i] > 1) { // nums[i]保留到1,把其他物品都展开 + weight.push_back(weight[i]); + value.push_back(value[i]); + nums[i]--; + } + } + + vector dp(knapsackWeight + 1, 0); + for (int i = 0; i < weight.size(); i++) { // 遍历物品 + for (int j = knapsackWeight; j >= weight[i]; j--) { // 遍历背包容量 + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); + } + } +} +```