Update knapsack
All checks were successful
ci/woodpecker/push/test Pipeline was successful

This commit is contained in:
Sainnhe Park 2023-02-09 17:48:46 +08:00
parent 8dc3a66883
commit b2dda568e2

View File

@ -201,3 +201,96 @@ void complete_knapsack_problem_1d() {
- 一种特殊情况是 `dp[0]`,它表示组合成总金额为 `0` 的组合数,我们必须把它赋值为 `1`,这是因为当我们执行 `dp[j] += dp[j - coins[i]]` 的时候,如果 `j == coins[i]` 那么显然应该自增 `1` - 一种特殊情况是 `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<int> weight = {1, 3, 4};
vector<int> value = {15, 20, 30};
vector<int> 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<int> 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]);
}
}
}
```