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

完全背包

完全背包理论基础

1. 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],价值是value[i] 。每件物品都有无限个(即可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

2. 「完全背包」与「01背包」唯一的不同:遍历顺序。01背包的内循环从大到小遍历,为了保证每个物品仅被添加一次;完全背包的物品可以添加多次,因此从小到大遍历;01背包中,二维dp数组的两个for遍历的先后循序可以颠倒,一维dp数组的两个for循环先后循序一定是先物品,再背包容量。完全背包中,一维dp数组的两个for循环嵌套顺序可以颠倒。

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

518. 零钱兑换II

力扣

思路:

1. 本题区别于纯完全背包问题之处:纯完全背包求凑成背包最大价值,本题求凑成总金额的物品组合个数。

2. dp数组以及下标的含义:dp[j]表示凑成总金额为j的货币组合个数为dp[j]。

3. 递推公式:dp[j]就是所有的dp[j-coins[i]]相加。因此,dp[j] += dp[j-coins[i]]。

4. 初始化:dp[0] = 1(递推公式的基础),其他下标的dp初始化为0。

5. 遍历顺序:外层for循环遍历物品(钱币),内层for遍历背包(金钱总额),得到的dp[j]为组合数;假如先外层for循环遍历背包(金钱总额),内层for循环遍历物品(钱币),得到的dp[j]就是排列数;因此,外层遍历物品(钱币),内层遍历背包(金钱总额)。

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount+1];
        dp[0]=1;
        for(int i=0;i

377. 组合总和 Ⅳ

力扣

思路:

1. 本题求凑成target的排列个数。类似上一题,区别在于上一题求组合个数,本题求排列个数;

2. dp数组及其下标含义:dp[i]表示凑成目标正整数为i的排列个数为dp[i];

3. 递推公式:dp[i] += dp[i-nums[j]];

4. 初始化:dp[0] = 1(递推公式的基础),其他下标的dp初始化为0;

5. 遍历顺序:外层for循环遍历背包(目标正整数),内层for循环遍历物品(数字)。

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        dp[0] = 1;
        for(int i=0;i<=target;i++){
            for(int j=0;j=nums[j]){
                    dp[i] += dp[i-nums[j]];
                }
            }
        }
        return dp[target];
    }
}

总结

求装满背包有几种方法,递归公式类似(+=),关键在于遍历顺序。如果求的是组合个数,外层for循环遍历物品,内层for循环遍历背包容量;如果求的是排列个数,外层for循环遍历背包容量,内层for循环遍历物品。

你可能感兴趣的:(算法)