美文网首页
LeetCode 第518题:零钱兑换 II

LeetCode 第518题:零钱兑换 II

作者: 放开那个BUG | 来源:发表于2020-11-23 09:19 被阅读0次

    1、前言

    题目描述

    2、思路

    本题使用完全背包的思路,那完全背包跟 0-1 背包有何区别呢?

    0-1 限制了物品的数量,物品并不是无限的。假设有物品 weight 跟物品的价值 value,weight_i 表示每个物品的重量,value_i 表示每个物品对应的价值,在不超过背包容量的情况下怎样才能使得价值最大?其实对于每个物品来说,都可以放与不放。那么定义一个二位数组 dp[i][j] 表示在选择物品 i 、背包容量为 j 的情况下价值最大。

    那么对于 dp[i][j] 来说,存在放第 i 个物品跟不放第 i 个物品。

    在不放第 i 个物品的情况下,直接继承上一个背包的数据情况,即 dp[i][j] = dp[i - 1][j]。

    在放第 i 个物品的情况下,那么需要加上第 i 个物品的价值 value_i[i - 1](数组从 0开始),然后需要加上去掉 i 的重量的情况下,应该由哪个数据推导过来,即 dp[i - 1][j - weight_i[i - 1]],所以 dp[i][j] = dp[i - 1][j - weight_i[i - 1]] + value_i[i - 1]。

    所以最大价值为 dp[i][j] = max {dp[i - 1][j], dp[i - 1][j - weight_i[i - 1]] + value_i[i - 1]}

    要牢牢记住一点,动态规划是自底向上,由已知推到未知。

    那么对应完全背包来说,物品的数量是无限的,那么也存在上面放与不放的情况。
    针对这题,针对物品 i,存在放0个,放1个,放 k 个,所以 dp[i][j] = dp[i - 1][j - 0 * coins[i]] + dp[i - 1][j - 1 * coins[i]] ....,而 dp[i][j - coins[i]] = dp[i][j - coins[i] - 0 * coins[i]] + dp[i][j - coins[i] - 1 * coins[i]] + ...。

    那么,上面两式相减得:dp[i][j]−dp[i][j−coins[i]]=dp[i−1][j],即 dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]

    3、代码

    初始代码:

    public class Solution {
    
        public int change(int amount, int[] coins) {
            int len = coins.length;
            if (len == 0) {
                if (amount == 0) {
                    return 1;
                }
                return 0;
            }
    
            int[][] dp = new int[len][amount + 1];
            // 题解中有说明应该如何理解这个初始化
            dp[0][0] = 1;
    
            // 填第 1 行
            for (int i = coins[0]; i <= amount; i += coins[0]) {
                dp[0][i] = 1;
            }
    
            for (int i = 1; i < len; i++) {
                for (int j = 0; j <= amount; j++) {
                    for (int k = 0; j - k * coins[i] >= 0; k++) {
                        dp[i][j] += dp[i - 1][j - k * coins[i]];
                    }
                }
            }
            return dp[len - 1][amount];
        }
    }
    

    优化后代码:

    
    class Solution {
        public int change(int amount, int[] coins) {
            int[][] dp = new int[coins.length + 1][amount + 1];
            for (int i = 0; i < dp.length; i++) {
                // 没有 amount 时,不用凑数,直接得1
                // dp[0][i] 为 0,但是数组默认都是0,所以不用写
                dp[i][0] = 1;
            }
    
            for(int i = 1; i <= coins.length; i++){
                for(int j = 1; j <= amount; j++){
                    if(j - coins[i - 1] < 0){
                        dp[i][j] = dp[i - 1][j];
                    }else {
                        dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]];
                    }
                }
            }
    
            return dp[coins.length][amount];
        }
    }
    
    

    相关文章

      网友评论

          本文标题:LeetCode 第518题:零钱兑换 II

          本文链接:https://www.haomeiwen.com/subject/rbkpiktx.html