85.零钱兑换

0 阅读1分钟

题目链接

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

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

解法1 记忆化递归

思路

这题要求最少得硬币个数,会想当然的去用贪心,我从最大的开始减,然后剩下的去递归计算。这看起来直觉上很对,但实际上这是错的策略,因为它不能保证一定得出最优解。

比如 conins = [1, 3, 4], amount = 6 ,如果从最大的开始减会得到 4 + 1 + 1 = 3 的答案,而不会得到 3 + 3 = 2 的答案。使用大的硬币这只在某些特定硬币组合下成立。

代码

function coinChange(coins: number[], amount: number): number {
    const memo = new Map();
    const dfs = (remain) => {
        if (remain === 0) return 0;
        if (remain < 0) return -1;
        if (memo.has(remain)) return memo.get(remain);

        let min = Infinity;
        for (let coin of coins) {
            const res = dfs(remain - coin);
            if (res >= 0) {
                min = Math.min(min, res + 1);
            }
        }

        const result = (min === Infinity) ? -1 : min;
        memo.set(remain, result);
        return result;
    };
    return dfs(amount);
};

时空复杂度

时间复杂度:O(amount * n)

空间复杂度:O(amount)

解法2 动态规划

思路

dp 数组表示的就是 amounti 时的最少硬币,需要从底向上开始计算。

代码

function coinChange(coins: number[], amount: number): number {
    const dp = new Array(amount + 1).fill(Infinity);
    dp[0] = 0;

    for (let i = 1; i <= amount; i++) {
        for (let coin of coins) {
            if (i - coin >= 0 && dp[i - coin] !== Infinity) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    return dp[amount] === Infinity ? -1 : dp[amount];
};

时空复杂度

时间复杂度:O(amount * n)

空间复杂度:O(amount)