动态规划:零钱兑换问题(javascript求解)

Posted _阿锋丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划:零钱兑换问题(javascript求解)相关的知识,希望对你有一定的参考价值。

前言

昨天天面试时突然提到一嘴动态规划,结果面试官问我动态规划是啥子,我竟然一时表达不出来,在这里用自己的话重复在练习一下:动态规划就是先找到大问题的子问题,将子问题的解作为中间结果递推求解最终问题,而且子问题的解要是局部最优的。

动态规划问题的一般解题步骤

  1. 确定状态:一般是可以用数组表示状态
  2. 化成子问题:涉及到问题的最后一步怎么求解的(比如跳台阶问题的最后一般可以调上一节台阶和跳上2节台阶得到 dp[n] = dp[n-1]+dp[n-2]
  3. 初始条件和边界情况需要注意 (设置 dp[0] = 1)
  4. 计算顺序 自底向上 (递归的话一般是自顶向下的顺序)

零钱兑换

题目描述

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

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

思路:

  • 先确定用dp[amount+1]数组表示使用的最小的硬币数量
  • 最后一步无论怎么样都是添加一个硬币coin使得金额刚好是amount ,然后在比较添加这枚和不添加这枚的硬币数谁少, 所以递推式min(dp[amount - coin] +1,dp[amount])
  • 返回结果 dp[amount]

js代码

var coinChange = function (coins, amount) 
// 用无穷大填充数组的每一个元素
  let dp = new Array(amount + 1).fill(Infinity)
  dp[0] = 0
  console.log(dp);
  let len = coins.length;

  for (let i = 0; i <= amount; i++) 
    for (let j = 0; j < len; j++) 
    // 注意:这里的条件左边是钱的价格肯定要比硬币的价额大才可以放进去的
    // 条件的右边是如果数组元素还是初始值肯定就是没有符合条件的硬币放
      if (i >= coins[j] && dp[i - coins[j]] !== Infinity) 
        dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
      
    
  
  if (dp[amount] === Infinity) 
    return -1;
  
  return dp[amount];
;


console.log(coinChange([1, 2, 5], 11));

零钱兑换 II

题目描述

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例 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

思路

其实这题和调台阶其实是同一类型的题

完全背包之组合问题——填满容量为amount的背包,有几种硬币组合

  • dp[j] 代表装满容量为j的背包有几种硬币组合 转移方程:dp[j] = dp[j] + dp[j - coin]
  • 当前填满j容量的方法数 = 之前填满j容量的硬币组合数 + 填满j - coin容量的硬币组合数 也就是当前硬币coin的加入,可以把j -coin容量的组合数加入进来 和01背包差不多,唯一的不同点在于硬币可以重复使用,一个逆序一个正序的区别 返回dp[-1],也就是dp[amount]

上面是看了别人的思路,然后我一开始是这么写的代码

var change = function (amount, coins) 
  // 用dp[i]存储有多少当前金额i下有多少中组合方式
  let dp = new Array(amount + 1).fill(0);
  dp[0] = 1

  for (let i = 1; i <= amount; i++) 
    for (let j = 0; j < coins.length; j++) 

      dp[i] = dp[i - coins[j]] + dp[i]
    
  

  return dp[amount];

;


console.log(change(5, [1, 2, 5]))

然后一直不对 ,结果比真实答案要大,后来我debuger 后知道这是求的排列数的结果,而答案是求组合数 ,并且我在leetcode翻到如下一句话

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

所以要改变一下遍历顺序

var change = function (amount, coins) 
  // 用dp[i]存储有多少当前金额i下有多少中组合方式
  let dp = new 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] + dp[j - coins[i]]
    
  

  return dp[amount];

;


console.log(change(5, [1, 2, 5]))

以上是关于动态规划:零钱兑换问题(javascript求解)的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode.322-零钱兑换(最大/小型动态规划)

Leetcode.322-零钱兑换(最大/小型动态规划)

322. 零钱兑换

322. 零钱兑换

leetcode 322. 零钱兑换

动态规划解题(leetcode322零钱兑换)