力扣518.零钱兑换---完全背包问题。

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

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

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

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

示例 1:
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:
输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:
输入:amount = 10, coins = [10]
输出:1

理解: 这是一种组合优化问题,我们所选的硬币之间没有什么特定的联系,我们只需要选择他们,并把他们凑成我们所需要的样子。
本题我们很容易就可以看出是完全背包。
背包问题的概念和解决方案

朴素解法: arr[i][j]定义为前 i 种硬币凑成总金额为 j 的方案总和。
显然对于第 i 种硬币我们拥有着两种选择方案:
1、直接不选:arr[i][j] = arr[i - 1][j]
2、由于可以选任意个(不超出范围的情况下),所以 :
arr[i][j] += arr[i - 1][j - k *value],0 <= k <= [j / value],value为当前硬币的价值。
附:arr[0][0] = 1, arr[0][x] = 0;

class Solution {
    public int change(int amount, int[] coins) {
        int length = coins.length;
        int[][] arr = new int[length + 1][amount + 1];
        arr[0][0] = 1;
        //循环,表示前 i 种硬币
        for(int i = 1; i <= length; i++){
            //表示前 i 种硬币,总金额为 j 的组合方案。
            for(int j = 0; j <= amount; j++){
                //状态转移。
                for(int k = 0; k * coins[i - 1] <= j; k++){
                    arr[i][j] += arr[i - 1][j - k * coins[i - 1]];
                }
            }
        }
        return arr[length][amount];
    }
}

空间优化: 上面那个解法的空间复杂度和时间复杂度都挺离谱的,让我们先去优化一下它的空间。
因为我们选取前 i 种硬币凑成金额为 j 的方案总数,其实只和前 i - 1种硬币的方案有关。

class Solution {
    public int change(int amount, int[] coins) {
        int[] arr = new int[amount + 1];
        arr[0] = 1;
        for(int value : coins){
            //表示前 i 种硬币,总金额为 j 的组合方案。
            //因为我们的arr[i][j]其实,只要考虑arr[i - 1][j - k*coins[i - 1]]
            //所以我们只需要从大到小改变,那么就不会对相对金额小的结果产生影响。
            for(int j = amount; j >= value; j--){
                //状态转移。
                for(int k = 1; k * value <= j; k++){
                    arr[j] += arr[j - k * value];
                }
            }
        }
        return arr[amount];
    }
}

进行时间上的优化: 从后往前改变是我们优化空间的常用办法,但是在优化种你会发现,arr[i][j] = arr[i - 1][j] + arr[i][j - value] (可以自己推到下)
所以:

class Solution {
    public int change(int amount, int[] coins) {
        int[] arr = new int[amount + 1];
        arr[0] = 1;
        for(int value : coins){
            for(int j = value; j <= amount; j++){
                arr[j] += arr[j - value];
            }
        }
        return arr[amount];
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值