凑零钱问题

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

解: 由于所需硬币数是不确定的,而且组合数也不定,所以需要穷举所有的组合。在这题中,硬币数是不定的,可以作为自变量n,硬币数n和总金额amount的对应关系为:

f(n) = amount

f(n)的最优解使用动态规划的最优子结构可以构造为f(n) = f(n-1) + coins[i],即找出n-1个硬币组合的最小值,也就是剩余金额的最小硬币数组合。使用自底向上迭代法重构f(n):
将金额数从0到amount作为自变量,求解当前金额下的最小硬币组合数的数学公式为:

f(i) = min{f(i), f(i-coin) + 1}, 其中:0 ≤ i ≤ amount+1,coin∈coins

根据上面的公式构建数组dp[amount+1],初始全置最大值amount+1是因为当dp[i] = amount+1时,最小硬币数组合查过了面额值amount,必然是没有满足amount的组合数,因此算法返回-1。

dp[i] = min(dp[i], dp[i - coin] + 1)

关于上面公式的解释:dp[i]表示当金额为i时的最小硬币数。dp[i - coin] + 1表示金额为 i 时,选择coin作为最小硬币数组合中的一个,所以金额 i 选定了一个面值为 coin 的硬币,数量确定了一个,需要 +1;而dp[i - coin] 为选定 coin 后剩余面额(i-coin)时的最小硬币数。

关于min(dp[i], dp[i - coin] + 1),为什么选择dp[i] 和 (dp[i - coin] + 1)中最小的一个呢?这是因为我们在初始时将整个dp数组除0位置外都赋值为amount+1,也就是说初始的时候dp[i] 和 dp[i - coin] 的值是相同的。如果给定硬币面额组合中没有满足,或刚好需要amount+1个硬币才满足金额为(i-coin)的时候,这个时候dp[i-coin]的是大于或等于dp[i],再加1就会大于dp[i],而我们需要的是最小组合数,此时需要选择dp[i]。

完整Java代码如下:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        Arrays.fill(dp, amount+1); //数组所有元素置最大值
        dp[0] = 0;
        for(int i = 0; i < dp.length; i++){
            for(int coin : coins){
                if(i < coin) continue;
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
        return (dp[amount] == (amount+1)) ? -1 : dp[amount];
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值