有N件物品和一个最多可以背重量为W的背包。第i件物品的重量是weight[i],价值是value[i]。每件物品都有无限个(可以放入背包多次)。
01背包的核心代码:
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]);
}
}
01背包内嵌循环是从大到小遍历,为了保证每个物品仅被添加一次。
完全背包的物品可以添加多次,所以要从小到大去遍历:
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]);
}
}
// 完全背包问题:每个物品可以无限次选择
void test_CompletePack(){
vector<int> weight = {1 , 3, 4}; // 物品重量
vector<int> value = {15 , 20 ,30}; // 物品价值
int bagWeight = 4; // 背包最大容量
vector<int> dp(bagWeight + 1, 0); // dp数组,初始化为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]); // 完全背包公式
}
}
cout << dp[bagWeight] << endl; // 输出结果
}
动规五部曲:
1.确定dp数组以及下标的含义
dp[j] : 凑成总金额j的货币组合数为dp[j]
2.确定递推公式
dp[j] 是所有的dp[j - coins[i]]相加。
递推公式:dp[j] +=dp[j -coins[i]];
3.dp数组如何初始化
dp[0] = 1,如果dp[0] = 0。后续所有的推导出来的值都是0。
4.确定遍历顺序
外层for循环遍历物品,内层for循环遍历背包。
for(int i = 0 ; i < coins.size() ; i++){ // 遍历物品
for(int j = coins[i] ; j <= amount ; j++){ // 遍历背包容量
dp[j] += dp[j - coins[i]];
}
}
class Solution {
public:
int change(int amount, vector<int>& coins) {
// 定义 dp 数组,长度为 amount+1,dp[i] 表示凑出金额 i 的方案数
vector<int> dp(amount + 1, 0);
// 初始状态,凑出 0 元有一种方案,即不选择任何硬币
dp[0] = 1;
// 遍历硬币
for (int i = 0; i < coins.size(); i++) {
// 遍历金额,从 coins[i] 开始是因为比 coins[i] 小的金额无法使用 coins[i] 来凑出
for (int j = coins[i]; j <= amount; j++) {
// 转移方程:dp[j] 表示当前凑出金额 j 的方案数,加上选择 coins[i] 后凑出金额 j 的方案数 dp[j - coins[i]]
dp[j] += dp[j - coins[i]];
}
}
// 返回凑出 amount 的方案数
return dp[amount];
}
};
动规五部曲:
1.确定dp数组以及下标的含义
dp[i] : 凑成目标正整数为i的排列个数为dp[i]
2.确定递推公式
递推公式:dp[i] += dp[i - nums[j]];
3.dp数组如何初始化
由递推公式可知dp[0]需要初始化为1,这样递归其他dp[i]时才会有数值基础。
4.确定遍历顺序
如果求组合数就是外层for循环遍历物品,内层for循环遍历背包。
如果求排列数就是外层for循环遍历背包,内层for循环遍历物品。
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, 0); // dp数组,初始化为0
dp[0] = 1; // 初始化dp[0]=1
for (int i = 0; i <= target; i++) { // 遍历背包容量
for (int j = 0; j < nums.size(); j++) { // 遍历物品
if (i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) { // 如果当前容量能够放下该物品,并且没有溢出
dp[i] += dp[i - nums[j]]; // 更新dp[i]
}
}
}
return dp[target]; // 返回装满背包的方案数
}
};
[代码随想录]