代码随想录算法训练营第四十四天|完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

134 阅读2分钟

完全背包

完全背包和01背包问题唯一不同的地方就是,完全背包中每种物品有无限件

完全背包和01背包在代码上唯一的不同体现在遍历顺序上,我们直接对两个问题的遍历顺序进行分析。

01背包在使用一维dp数组时,是先遍历物品,然后倒序遍历背包容量

01背包中之所以倒序去遍历背包容量,是为了防止重复放入物品。而完全背包中的物品的可以被放入无限次的。所以完全背包中我们的遍历顺序应该是从小到大遍历背包容量

而对于纯完全背包的问题,先遍历物品还是先遍历背包容量都是没有关系的。但是对于特定的应用题目,我们需要具体来分析应该如何进行遍历。

//先遍历物品,再遍历背包
private static void testCompletePack(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 0; i < weight.length; i++){ // 遍历物品
        for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

//先遍历背包,再遍历物品
private static void testCompletePackAnotherWay(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
        for (int j = 0; j < weight.length; j++){ // 遍历物品
            if (i - weight[j] >= 0){
                dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
            }
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

518. 零钱兑换 II

题目链接:518. 零钱兑换 II

思路:本题求的是能够凑成总金额的硬币组合数,完全背包解决组合问题

动态规划五步曲:

  1. dp[j] 表示可以组合成总额为j的组合数有dp[j]种
  2. 求组合问题的递推公式:dp[j] += dp[j - coins[i]]; 昨天也提到过。
  3. 初始化,组合问题,初始化dp[0] = 1。
  4. 遍历顺序,本题是求解组合数,应该先遍历物品,再遍历背包容量

如果是求解排列数,则应该先遍历背包容量,再遍历物品

5.举例说明

class Solution {
    public int change(int amount, int[] coins) {
        // dp[j] 表示可以组合成总额为j的组合数有dp[j]种
        int[] dp = new int[amount + 1];
        // 递推公式
        // 初始化
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
}

377. 组合总和 Ⅳ

题目链接:377. 组合总和 Ⅳ

思路:本题其实是一个求排列的问题。本题只需要求排列总和的个数,可以使用背包,如果要将所有排列列举出来,只能用回溯暴搜。

动态规划五步曲:

  1. dp[j] 表示能够组成j的组合数有dp[j]个
  2. 排列问题与求组合问题的递推公式相同,遍历顺序不同。递推公式为:dp[j] += dp[j - nums[i]];
  3. 初始化,dp[0] = 1
  4. 求解排列数,先遍历背包容量,再遍历物品。
  5. 举例说明
class Solution {
    public int combinationSum4(int[] nums, int target) {
        // dp[j] 表示能够组成j的组合数有dp[j]个
        int[] dp = new int[target + 1];
        // 递推公式:dp[j] += dp[j - nums[i]]
        // 初始化
        dp[0] = 1;
        for (int j = 0; j <= target; j++) {
            for (int i = 0; i < nums.length; i++) {
                if (j >= nums[i]) dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
}