求取 dp14 有两种情况:
- 放物品1
- 还是不放物品1
如果不放物品1, 那么背包的价值应该是 dp04 即 容量为4的背包,只放物品0的情况。
推导方向如图:

如果放物品1, 那么背包要先留出物品1的容量,目前容量是4,物品1 的容量(就是物品1的重量)为3,此时背包剩下容量为1。
容量为1,只考虑放物品0 和物品1 的最大价值是 dp11, 注意 这里和 01背包理论基础(二维数组) 有所不同了 !一个是(i-1)意义是放了i这个后,剩下i-1的最大值。完全背包不用减1,放了i之后依旧是0-i的最大值
在 01背包理论基础(二维数组) 中,背包先空留出物品1的容量,此时容量为1,只考虑放物品0的最大价值是 dp01,因为01背包每个物品只有一个,既然空出物品1,那背包中也不会再有物品1!
而在完全背包中,物品是可以放无限个,所以 即使空出物品1空间重量,那背包中也可能还有物品1,所以此时我们依然考虑放 物品0 和 物品1 的最大价值即: dp11, 而不是 dp01
所以 放物品1 的情况 = dp11 + 物品1 的价值,推导方向如图:

cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,bagweight;
cin>>n>>bagweight;
vector<int>weight(n,0);
vector<int>value(n,0);
for(int i=0;i<n;i++){
cin>>weight[i]>>value[i];
}
vector<vector<int>>dp(n,vector<int>(bagweight+1,0));
for(int i=0;i<n;i++){
dp[i][0]=0;
}
for (int j = weight[0]; j <= bagweight; j++){
dp[0][j] = dp[0][j - weight[0]] + value[0];
}
for(int i=1;i<n;i++){
for(int j=1;j<=bagweight;j++){
if(weight[i]>j)dp[i][j]=dp[i-1][j];//不要忘记
else dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
}
}
cout<<dp[n-1][bagweight];
}
零钱兑换2:
二维:
cpp
class Solution {
public:
int change(int amount, vector<int>& coins) {
//dp[i][j]用前0-i个物品装满amout大小的背包,有多少种方法
vector<vector<unsigned long long>>dp(coins.size(),vector<unsigned long long>(amount+1,0));
if(coins[0]>amount)return 1;
for(int i=0;i<coins.size();i++){
dp[i][0]=1;}
for (int j = 0; j <= amount; j++) {
if (j % coins[0] == 0) dp[0][j] = 1;
}
for(int i=1;i<coins.size();i++){
for(int j=1;j<=amount;j++){
if(coins[i]>j)dp[i][j]=dp[i-1][j];
else dp[i][j]=dp[i-1][j]+dp[i][j-coins[i]];//相加,不是max
}
}
return dp[coins.size()-1][amount];
}
};
一维:
dp[j] += dp[j - coins[i]]
怎么理解这个式子?
题目设定 :
硬币 coins = [1, 2],目标总金额 amount = 4。
(最终答案应该是 3 种组合:[1,1,1,1]、[1,1,2]、[2,2])
我们一步步执行代码 dp[j] += dp[j - coins[i]],盯着 dp[4] 这个格子,看它到底是怎么被"新增"出来的。
🟦 初始状态
dp[0] = 1(凑0元有一种方法:啥也不拿)。
其他 dp[1] 到 dp[4] 全是 0。
| 金额 j | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| dpj | 1 | 0 | 0 | 0 | 0 |
🟩 第一轮:处理硬币 1 (面值 1)
(正序循环,j 从 1 到 4)
-
j=1:dp[1] += dp[0]→dp[1] = 0 + 1 = 1。(凑1的方案:[1]) -
j=2:dp[2] += dp[1]→dp[2] = 0 + 1 = 1。(凑2的方案:[1,1]) -
j=3:dp[3] += dp[2]→dp[3] = 0 + 1 = 1。(凑3的方案:[1,1,1]) -
j=4:dp[4] += dp[3]→dp[4] = 0 + 1 = 1。(凑4的方案:[1,1,1,1])
第一轮结束后的表格:
| 金额 j | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| dpj | 1 | 1 | 1 | 1 | 1 |
此时,dp[4] 里的这 1 种方法,是只用硬币 1 凑出来的老方法 [1,1,1,1]。
🟧 第二轮:处理硬币 2 (面值 2)
(这是最关键的一步!正序循环,j 从 2 到 4)
① 计算 j = 2:
dp[2] = dp[2] + dp[0] = 1 + 1 = 2
-
左边的旧
dp[2](不选) :是1,代表旧方案[1,1]。 -
右边的新增
dp[0](选) :是1,代表剩余0元的方法(空集)+ 硬币2 = 新方案[2]。 -
结果 :凑2现在有 2 种方法:
[1,1]和[2]。
| 金额 j | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| dpj | 1 | 1 | 2 | 1 | 1 |
② 计算 j = 3:
dp[3] = dp[3] + dp[1] = 1 + 1 = 2
-
左边的旧
dp[3](不选) :是1,代表旧方案[1,1,1]。 -
右边的新增
dp[1](选) :dp[1]此时是 1 (来自上一轮,没被硬币2污染,因为 1 < 2)。它代表剩余1元的方法[1]+ 硬币2 = 新方案[1,2]。 -
结果 :凑3现在有 2 种方法:
[1,1,1]和[1,2](注意顺序固定,没有[2,1])。
| 金额 j | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| dpj | 1 | 1 | 2 | 2 | 1 |
③ 计算 j = 4(你困惑的焦点!):
dp[4] = dp[4] + dp[2] = 1 + 2 = 3
我们把左右拆开看:
-
左边的旧
dp[4](不选硬币2) :值是
1。它代表原来凑成 4 的老方法 ,即上一轮留下的[1,1,1,1]。这些方法不动,继续保留。 -
右边的新增
dp[2](选硬币2) :注意! 这里的
dp[2]不是上一轮的1,而是当前这一轮刚刚算出来的2!这
2代表着凑成剩余金额2的 2 种方法:-
方法A:
[1,1]+ 硬币2 = 得到新方案[1,1,2] -
方法B:
[2]+ 硬币2 = 得到新方案[2,2]
-
你看 :dp[4] 原来是 [1,1,1,1](1种)。
通过 += dp[2],我们新增 了 [1,1,2] 和 [2,2](2种)。
所以总数变成了 1 + 2 = 3 种。
| 金额 j | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| dpj | 1 | 1 | 2 | 2 | 3 ✅ |