力扣322题:零钱兑换。用动态规划的方法

198 阅读3分钟

这段代码是一个动态规划算法,用于解决找零钱问题(Coin Change Problem)。该问题的描述是给定不同面额的硬币和一个总金额,找出可以凑成总金额的最少硬币数。

首先,让我们逐行解析代码:

var coinChange = function(coins, amount) {
    const f = [];
    f[0] = 0;

    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);
            }
        }
    }

    if (f[amount] === Infinity) {
        return -1;
    }
    return f[amount];
};
  1. const f = [];: 创建一个数组 f,用于存储凑成各个金额所需的最少硬币数量。
  2. f[0] = 0;: 初始化 f[0] 为 0,表示凑成金额为 0 时不需要硬币。
  3. for(let i = 1; i <= amount.length; i++) {: 通过循环遍历金额范围,从 1 到总金额。
  4. f[i] = Infinity;: 初始化凑成金额 i 所需的最少硬币数量为无穷大,后续会用动态规划更新这个值。
  5. for(let j = 0; j < coins.length; j++) {: 遍历硬币数组。
  6. if(i - coins[j] >= 0) {: 检查当前硬币面额是否小于等于当前金额 i,确保不越界。
  7. f[i] = Math.min(f[i], f[i - coins[j]] + 1);: 更新凑成金额 i 所需的最少硬币数量,取当前值和使用当前硬币后的值的较小者。这是动态规划的关键步骤。
  8. if(f[amount] == Infinity) { return -1; }: 如果凑成总金额的最小硬币数量仍然是无穷大,则表示无法凑成该金额,返回 -1。
  9. return f[amount];: 返回凑成总金额所需的最少硬币数量。

什么是动态规划

动态规划(Dynamic Programming,简称DP)是一种通过将原问题分解为相对简单的子问题来求解复杂问题的方法。它通常用于优化问题,其中问题的最优解可以通过子问题的最优解来有效计算。

动态规划的基本思想是将原问题分解为子问题,并将子问题的解存储起来以避免重复计算。通过建立一个表格(通常是数组或矩阵),动态规划算法会填充表格中的条目,以逐步解决更大规模的原问题。

动态规划常用于解决那些具有重叠子问题和最优子结构性质的问题。这两个性质是动态规划问题的关键特征:

  1. 重叠子问题(Overlapping Subproblems): 原问题可以被分解为相似的子问题,这些子问题会被反复解决。
  2. 最优子结构(Optimal Substructure): 原问题的最优解可以通过利用子问题的最优解来构造。

动态规划有两种主要的方法:

  1. 自顶向下(Top-Down): 也称为记忆化搜索,通过递归的方式解决问题,但会在递归过程中保存子问题的解,避免重复计算。这样的方法通常使用递归加缓存(通常是一个表格或字典)来实现。
  2. 自底向上(Bottom-Up): 从子问题的最小规模开始,逐步构建更大规模问题的解。这种方法通常涉及填充表格或数组,从小规模问题开始直到解决原问题。

动态规划被广泛应用于各种领域,包括算法优化、组合优化、经济学、生物学等。一些经典的动态规划问题包括最长公共子序列、最短路径、0/1背包问题等。