完全背包
思路:
和01背包不同的点在于,完全背包中的物品是可以重复使用的。01背包和完全背包唯一不同就是体现在遍历顺序上。
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]);
}
}
而完全背包的物品是可以添加多次的,所以要从小到大去遍历
// 先遍历物品,再遍历背包
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]);
}
}
在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序同样无所谓!
518. 零钱兑换 II
题目链接:518. 零钱兑换 II
思路:
这题求的是组合总数。
动规五部曲
1.确定dp数组以及下标的含义
dp[j]的定义为:凑成总金额j的货币组合数为dp[j]
2.确定递推公式
求装满背包有几种方法,一般公式都是:dp[j] += dp[j - nums[i]];
dp[j] = dp[j](考虑coins[i]的组合总和)+ dp[j - coins[i]](不考虑coins[i]);
3.dp数组如何初始化
dp[0] = 1是 递归公式的基础。
4.确定遍历顺序
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
本题要求凑成总和的组合数,元素之间要求没有顺序,所以外层for循环是物品
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[][] dp = new int[n + 1][amount + 1];
// base case
for (int i = 0; i <= n; i++)
dp[i][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= amount; j++)
if (j - coins[i-1] >= 0)
dp[i][j] = dp[i - 1][j]
+ dp[i][j - coins[i-1]];
else
dp[i][j] = dp[i - 1][j];
}
return dp[n][amount];
}
}
377. 组合总和 Ⅳ
题目链接:377. 组合总和 Ⅳ
思路:
这题题目写的是组合,但是实际上是求排列。
动规五部曲
1.确定dp数组以及下标的含义
dp[i]: 凑成目标正整数为i的排列个数为dp[i]
2.确定递推公式
求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];
3.dp数组如何初始化
dp[0] = 1
4.确定遍历顺序
求排列数外层for遍历背包,内层for循环遍历物品。
class Solution {
public int combinationSum4(int[] nums, int target) {
// dp数组的定义 dp[j]从数组中找出返回总和为j的组合个数
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 0; i <= target; i++) {
for (int j = 0; j < nums.length; j++) {
if (i >= nums[j]) dp[i] = dp[i] + dp[i - nums[j]];
}
}
return dp[target];
}
}