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

49 阅读3分钟

完全背包

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

思路

完全背包和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]);

    }
}

01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。

在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序是无所谓的!

因为dp[j] 是根据 下标j之前所对应的dp[j]计算出来的。 只要保证下标j之前的dp[j]都是经过计算的就可以了。

// 先遍历物品,在遍历背包
function BagProblem(weight, value, size){
    let len = weight.length
    let dp = Array(size+1).fill(0)
    
    for(let i =0; i<len; i++){ //遍历物品
        for(let j=weight[i]; j<=size; j++){ //遍历背包
            dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i])
        }
    }
    return dp[size]
}

// 先遍历背包,再遍历物品
function BagProblem(weight, value, size){
    let len = weight.length
    let dp = Array(size+1).fill(0)
    
    for(let j=0; j<=size; j++){ //遍历背包
        for(let i=0; i<=len; i++){ //遍历物品
            if (j >= weight[i]) {
                dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i])
            }
        }
    }
    return dp[size]
}

518. 零钱兑换 II

题目链接

要求:给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。

思路

dp[j]:凑成总金额j的货币组合数为dp[j]

递推公式:dp[j] += dp[j - coins[i]]
dp[j] 就是所有的dp[j - coins[i]](考虑coins[i]的情况)相加

dp数组初始化: dp[0] = 1
如果dp[0] = 0 的话,后面所有推导出来的值都是0了。

var change = function(amount, coins) {
    let dp = Array(amount+1).fill(0)
    dp[0] = 1
    for(let i=0; i<coins.length; i++){
        for(let j=coins[i]; j<=amount; j++){
            dp[j] += dp[j-coins[i]]
        }
    }
    return dp[amount]
};

377. 组合总和 Ⅳ

题目链接

要求:给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

思路

本题要求的是排列,所以需要注意遍历顺序

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

所以本题外层是背包,内层是物品。

var combinationSum4 = function(nums, target) {
    let dp = Array(target+1).fill(0)
    dp[0] = 1
    for(let j=0; j<=target; j++){
        for(let i=0; i<nums.length; i++){
            if (j - nums[i] >= 0){
                dp[j] += dp[j-nums[i]]
            }
        }
    }
    return dp[target]
};