0-1背包和完全背包

---

### **背包问题全面总结**

---

#### **一、基本类型**
1. **0-1背包**  
   - **特点**:每个物品只能选一次。  
   - **状态转移**:  
     - 二维形式:`dp[i+1][c] = max(dp[i][c], dp[i][c-w[i]] + v[i])`  
     - 一维优化:倒序遍历容量(避免覆盖上一轮结果)  
     ```python
     for i in range(n):
         for c in range(target, w[i]-1, -1):
             dp[c] = max(dp[c], dp[c-w[i]] + v[i])
     ```

2. **完全背包**  
   - **特点**:物品可无限次选。  
   - **状态转移**:  
     - 二维形式:`dp[i+1][c] = max(dp[i][c], dp[i+1][c-w[i]] + v[i])`  
     - 一维优化:正序遍历容量(允许重复选择)  
     ```python
     for i in range(n):
         for c in range(w[i], target+1):
             dp[c] = max(dp[c], dp[c-w[i]] + v[i])
     ```

---

#### **二、常见变种问题**
1. **组合数问题**  
   - **目标**:计算达到背包容量的不同组合个数。  
   - **核心方程**:  
     - 完全背包:`dp[c] += dp[c - x]`  
     - 初始化:`dp[0] = 1`(空集为一种方案)  
   - **示例**:零钱兑换 II(LeetCode 518)  
     ```python
     dp = [0] * (amount+1)
     dp[0] = 1
     for x in coins:
         for c in range(x, amount+1):
             dp[c] += dp[c-x]
     ```

2. **最小物品数问题**  
   - **目标**:求达到容量的最少物品数。  
   - **核心方程**:  
     - `dp[c] = min(dp[c], dp[c-x] + 1)`  
     - 初始化:`dp[0] = 0`,其余为 `inf`  
   - **示例**:零钱兑换(LeetCode 322)  
     ```python
     dp = [inf] * (amount+1)
     dp[0] = 0
     for x in coins:
         for c in range(x, amount+1):
             dp[c] = min(dp[c], dp[c-x]+1)
     ```

3. **最大价值问题**  
   - **目标**:在容量限制下获取最大价值。  
   - **核心方程**:  
     - 0-1背包:`dp[c] = max(dp[c], dp[c-w] + v)`  
     - 初始化:`dp[0] = 0`,其余为 `-inf`(若要求恰好装满)  
   - **示例**:经典背包问题  
     ```python
     dp = [0] * (capacity+1)
     for w, v in items:
         for c in range(capacity, w-1, -1):
             dp[c] = max(dp[c], dp[c-w] + v)
     ```

---

#### **三、关键技巧**
1. **空间优化**  
   - 0-1背包 → 倒序遍历容量(防止覆盖)  
   - 完全背包 → 正序遍历容量(允许重复选)  

2. **初始化**  
   - **组合数**:`dp[0] = 1`  
   - **最小物品数**:`dp[0] = 0`,其余为 `inf`  
   - **恰好装满**:`dp[0] = 0`,其余为 `-inf`(最大价值问题)  

3. **边界条件**  
   - 容量需满足 `c >= x` 才能选择物品。  
   - 注意题目是否允许“不装满”(如初始化全为 0)。  

---

#### **四、常见问题解答**
1. **如何区分 0-1背包和完全背包?**  
   - 看物品是否可重复选。  
   - 遍历顺序:0-1背包倒序,完全背包正序。  

2. **如何处理恰好装满?**  
   - 初始化时,仅 `dp[0] = 0`(有效状态),其余为 `-inf`(无效状态)。  

3. **多维背包问题?**  
   - 扩展状态维度,如 `dp[c][k]` 表示容量为 `c`、已选 `k` 个物品时的最优解。  

---

#### **五、代码模板速查**
| 问题类型               | 代码框架                                                                 |
|------------------------|--------------------------------------------------------------------------|
| **0-1背包(最大价值)** | 倒序遍历容量,状态转移取 `max(dp[c], dp[c-w]+v)`                        |
| **完全背包(组合数)**  | 正序遍历容量,状态转移为 `dp[c] += dp[c-x]`                             |
| **最小物品数**          | 正序/倒序取决于背包类型,状态转移取 `min(dp[c], dp[c-x]+1)`             |

---

### 0 - 1背包问题C++代码示例 0 - 1背包问题中,有N件物品一个容量为C的背包,第i件物品的费用是v[i],价值是w[i],需求解将哪些物品装入背包可使这些物品的费用总不超过背包容量,且价值总最大。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int knapsack01(const vector<int>& weight, const vector<int>& value, int bagWeight) { vector<int> dp(bagWeight + 1, 0); for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); } } return dp[bagWeight]; } int main() { vector<int> weight = {1, 3, 4}; vector<int> value = {15, 20, 30}; int bagWeight = 4; int result = knapsack01(weight, value, bagWeight); cout << "0 - 1背包问题的最大价值: " << result << endl; return 0; } ``` ### 完全背包问题C++代码示例 完全背包问题与0 - 1背包问题的区别在于,完全背包问题中每种物品可以无限次选取。完全背包问题的遍历顺序是物品背包都是正序。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int completeKnapsack(const vector<int>& weight, const vector<int>& value, int bagWeight) { vector<int> dp(bagWeight + 1, 0); for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量,正序 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); } } return dp[bagWeight]; } int main() { vector<int> weight = {1, 3, 4}; vector<int> value = {15, 20, 30}; int bagWeight = 4; int result = completeKnapsack(weight, value, bagWeight); cout << "完全背包问题的最大价值: " << result << endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值