- 完全背包
- 518. 零钱兑换 II
- 377. 组合总和 Ⅳ
第一题:完全背包
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
跟01背包的实现区别在哪里?
答:
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
for(int i = 0; i < weight.size(); i++) { // 遍历物品
if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
物品是从小到大遍历,装到装不了然后再从小的开始。
第二题: 零钱兑换 II
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例 1:
- 输入: amount = 5, coins = [1, 2, 5]
- 输出: 4
解释: 有四种方式可以凑成总金额:
- 5=5
- 5=2+2+1
- 5=2+1+1+1
- 5=1+1+1+1+1
钱币数量不限,说明是完全背包。不过本题要实现的目标是凑成总金额的组合个数。
(1)确定dp数组以及下标的含义
dp[j]:凑成总金额的货币组合数为dp[j]
(2)确定递推公式
dp[j]就是所有dp[j-weight[i]]的相加
dp[j]+=dp[j-coins[i]]; 金币j上的组合数等于没加这个金币之前的所有组合数之和
(3)dp数组如何初始化
dp[0]=1 0容量也是一种容量。
(4)确定遍历顺序
本题求的是组合数,不考虑排列的问题,所以应该是先遍历物品,再遍历容量
for (int i = 0; i < coins.size(); i++) { // 遍历物品
for (int j = coins[i]; j <= amount; j++) { // 遍历背包
dp[j] += dp[j - coins[i]];
}
}
(5)举例推导dp数组
先遍历物品,再遍历背包,也就是先横着遍历,再往第二层遍历。
第三题:组合总和Ⅳ
给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。
示例:
- nums = [1, 2, 3]
- target = 4
所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)
本题还是求组合的个数,类似前面的可重复的硬币,但是硬币是组合问题,本题是排列问题。
动态规划五部曲
(1)确定dp数组及其下标含义
dp[i]:凑成目标正整数i的排列个数。
(2)确定递推公式:
如同硬币题,dp[i]+=dp[i-nums[j]];
dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。
因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。
(3)dp数组如何初始化
这种递推公式的前提下,大部分dp[0]=1
(4)确定遍历顺序
如果求组合数就是外层for循环遍历物品,内层for循环遍历背包;
如果求排列数就是外层for循环遍历背包,内层for循环遍历物品。
(5)举例推导数组