DAILY DOCDAILY DOC
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
Rust
Node
Notes
Ubuntu
Leetcode
  • it-tools
  • excalidraw
  • linux-command
  • Array

    • 数组
    • 二分查找
    • moveZeros
  • Dynamic-programming

    • 动态规划
  • 刷题指南
  • String

    • 字符串
  • bitwise-operator

    • 位运算符
  • heap
  • history

    • [1014] 最佳观光组合
    • [1022] 从根到叶的二进制数之和
    • [104] 二叉树的最大深度
    • [11] 盛最多水的容器
    • [110] 平衡二叉树
    • [1227] 飞机座位分配概率
    • [129] 求根节点到叶节点数字之和
    • [1306] 跳跃游戏 III
    • [148] 排序链表
    • 155.最小栈
    • [165] 比较版本号
    • 1763. 最长的美好子字符串
    • [1870] 准时到达的列车最小时速
    • [199] 二叉树的右视图
    • [21] 合并两个有序链表
    • 215.数组中的第 k 个最大元素
    • [2306] 公司命名
    • [234] 回文链表
    • [2516] 每种字符至少取 K 个
    • [316] 去除重复字母
    • [3171] 找到按位或最接近 K 的子数组
    • [322] 零钱兑换
    • [41] 缺失的第一个正数
    • [44] 通配符匹配
    • [494] 目标和
    • [509] 斐波那契数
    • [518] 零钱兑换 II
    • [62] 不同路径
    • [676] 实现一个魔法字典
    • 70 爬楼梯
    • [718] 最长重复子数组
    • [78] 子集
    • [82] 删除排序链表中的重复元素 II
    • [871] 最低加油次数
    • [88] 合并两个有序数组
    • [887] 鸡蛋掉落
    • 958.二叉树的完全性检验
    • [98] 验证二叉搜索树
    • [983] 最低票价
    • leetcode practice
    • 约瑟夫问题
    • 移除重复节点
  • linked-list

    • 706. 设计哈希映射
    • 链表
  • stack

    • stack
  • tree

    • Tree Traversal
    • 二叉树的最近公共祖先
    • 二叉树
    • 题目
  • leetCode 刷题
  • 回溯
  • 排序算法

[518] 零钱兑换 II

回溯

details
var change = function (amount, coins) {
    let ans = 0;
    backtrack(0, amount);
    function backtrack(sum, max) {
        if (sum === max) {
            ans++;
            return;
        }
        if (sum > max) {
            return;
        }
        for (const coin of coins) {
            backtrack(sum + coin, max);
        }
    }
    return ans;
};

如上代码没有考虑重复问题,导致结果不对

[1, 1, 1, 1], [1, 1, 1, 1];
var change = function (amount, coins) {
    let ans = 0;
    backtrack(0, amount, 0);

    function backtrack(sum, max, startIndex) {
        if (sum === max) {
            ans++;
            return;
        }
        if (sum > max) {
            return;
        }
        for (let i = startIndex; i < coins.length; i++) {
            backtrack(sum + coins[i], max, i);
        }
    }
    return ans;
};

记忆优化

var change = function (amount, coins) {
    let memo = new Map();
    return backtrack(0, amount, 0);

    function backtrack(sum, max, startIndex) {
        if (sum === max) {
            return 1;
        }
        if (sum > max) {
            return 0;
        }
        const key = `${sum}-${max}-${startIndex}`;
        if (memo.has(key)) return memo.get(key);

        let count = 0;
        for (let i = startIndex; i < coins.length; i++) {
            let result = backtrack(sum + coins[i], max, i);
            count += result;
        }
        memo.set(key, count);
        return count;
    }
};

动态规划

details

动态规划思路:

  1. 定义状态:

    • 使用 dp[i] 表示金额为 i 时的组合总数。
  2. 状态转移方程:

    • 对于每一种硬币 coin,更新 dp[i],即从 dp[i - coin] 转移过来。
    • 具体公式为:dp[i] += dp[i - coin],表示考虑当前硬币 coin 后,i 金额的组合方式等于不考虑 coin 时的组合方式加上减去 coin 面额后的组合方式。
  3. 初始化:

    • 由于金额为 0 时的组合方式只有一种,即什么都不选,所以 dp[0] = 1。
  4. 遍历顺序:

    • 遍历硬币数组,对于每个硬币,从金额 coin 遍历到 amount,更新每个 dp[i] 的值。
var change = function (amount, coins) {
    // 定义 dp 数组,dp[i] 表示凑成金额 i 的组合数
    let dp = new Array(amount + 1).fill(0);
    dp[0] = 1; // 凑成金额 0 有一种方法:不使用任何硬币

    // 遍历每个硬币
    for (let coin of coins) {
        // 对于每个硬币,从 coin 到 amount 更新 dp 数组
        for (let i = coin; i <= amount; i++) {
            dp[i] += dp[i - coin];
        }
    }

    return dp[amount];
};
Last Updated:
Contributors: rosendo
Prev
[509] 斐波那契数
Next
[62] 不同路径