动态规划-零钱问题自我理解

338 阅读2分钟

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

动态规划解决方案

const coinChange = function(coins, amount) {
    // 用于保存每个目标总额对应的最小硬币个数
    const f = []
    // 提前定义已知情况
    f[0] = 0
    // 遍历 [1, amount] 这个区间的硬币总额
    for(let i=1;i<=amount;i++) {
        // 求的是最小值,因此我们预设为无穷大,确保它一定会被更小的数更新
        f[i] = Infinity
        // 循环遍历每个可用硬币的面额
        for(let j=0;j<coins.length;j++) {
            // 若硬币面额小于目标总额,则问题成立
            if(i-coins[j]>=0) {
                // 状态转移方程
                f[i] = Math.min(f[i],f[i-coins[j]]+1)
            }
        }
    }
    // 若目标总额对应的解为无穷大,则意味着没有一个符合条件的硬币总数来更新它,本题无解,返回-1
    if(f[amount]===Infinity) {
        return -1
    }
    // 若有解,直接返回解的内容
    return f[amount]
};
}

拍平代码来理解代码实现

输入[1,2,5]和总额11

  • 总额为1时,最少需要几个硬币?
    • f[1]=f[0]+1* 1元硬币
    • f[1]=f[0]+1* 2元硬币 因为一元不可能由2元硬币组成,不用考虑(也就是不会走到 if (i - coins[c] >= 0) 这里)
    • f[1]=f[0]+1* 5元硬币 因为一元不可能由5元硬币组成,不用考虑
  • 总额为2时,最少需要几个硬币?
    • f[2]=f[1]+1* 1元硬币 f[1]已经在前面算出最少为1,所以f[2]=1+1=2枚
    • f[2]=f[0]+1* 2元硬币 需要1枚
    • f[2]=f[0]+1*5元硬币 2元不可能由5元硬币组成,不考虑
  • 总额为3时,最少需要几个硬币?
    • f[3]=f[2]+1* 1元硬币 f[2]已经在前面算出最少为1枚,所以f[3]=1+1=2枚
    • f[3]=f[1]+1* 2元硬币 f[1]已经在前面算出最少为1枚,所以f[3]=1+1=2枚
    • f[3]=f[0]+1*5元硬币 3元不可能由5元硬币组成,不考虑
  • 省略
  • 总额为5时,最少需要几个硬币?
    • f[5]=f[4]+1* 1元硬币 f[4]已经在前面算出最少为2枚,所以f[4]=2+1=3枚
    • f[5]=f[3]+1* 2元硬币 f[3]已经在前面算出最少为2枚,所以f[3]=1+1=3枚
    • f[5]=f[0]+1* 5元硬币 f[0]已经在前面算出最少为0枚,所以f[5]=0+1=1枚
  • 省略
  • 以此类推 总额为11的时候,至少需要多少硬币呢?
    • f[11]=f[10]+1个* 1元硬币 f[10]之前算出是最少需要2枚 所以此种方案需要3枚
    • f[11]=f[9]+1个* 2元硬币 f[9]之前算出是最少需要3枚 所以此种方案需要4枚
    • f[11]=f[6]+1个* 5元硬币 f[6]之前算出是最少需要2枚 所以此种方案需要3枚

所以最后至少需要3枚硬币