算法学习记录(八十)

94 阅读1分钟

322. 零钱兑换

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

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

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

解:

一开始的想法是递归参数为i(当递归来到硬币组的第i个元素时)和sum(之前所选择的累加和)。当i越界时判断sum是否为target,如果是就返回0,表明不再需要硬币了,如果不是返回Infinity,表示决策失败。遍历尝试选择0~n个第i种硬币的情况,递归下去,取最小值返回。

改成动态规划之后,第三层的遍历即硬币个数的循环想不到斜率优化的方式,所以这个方案超时了。

const minCoin = (nums, target) => {
    const res = getRes(0, 0)
    return res === Infinity ? -1 : res
    function getRes(i, sum) {
        // 当把所有硬币都试过之后
        if (i === nums.length) {
            // 如果累加和是需要的结果,那么返回0表示成功,否则返回Infinity表示失败
            return sum === target ? 0 : Infinity
        }
        // 累加的过程中超过目标也返回Infinity表示失败
        if (sum > target) {
            return Infinity
        }
        let res = Infinity
        // 依次尝试当前硬币要选几枚
        for (let idx = 0; idx * nums[i] + sum <= target; idx++) {
            // 选几枚就加上多少作为硬币总量,然后选择最小的结果
            res = Math.min(getRes(i + 1, sum + nums[i] * idx) + idx, res)
        }
        return res
    }
}


const minCoin = (nums, target) => {
    const dp = []
    for (let i = 0; i <= nums.length; i++) {
        dp[i] = []
    }
    dp[nums.length][target] = 0
    for (let i = nums.length - 1; i >= 0; i--) {
        for (let j = target; j >= 0; j--) {
            dp[i][j] = Infinity
            for (let k = 0; k * nums[i] <= target - j; k++) {
                dp[i][j] = Math.min((dp[i + 1][k * nums[i] + j] ?? Infinity) + k, dp[i][j])
            }
        }
    }
    return dp[0][0] === Infinity ? -1 : dp[0][0]
}

重新设计递归参数,只有一个sum(之前的累加和)。递归函数返回达成sum还需要最少多少硬币。当累加和来到target时,返回0,表示不再需要硬币了。否则遍历硬币组,依次尝试递归,并且硬币数加一,返回最小结果。

const minCoin = (nums, target) => {
    const res = getRes(0)
    return res === Infinity ? -1 : res
    function getRes(sum) {
        if (sum >= target) {
            return 0
        }
        let res = Infinity
        for (let i = 0; i < nums.length; i++) {
            res = Math.min(getRes(sum + nums[i]) + 1, res)
        }
        return res
    }
}


const coinChange = function(nums, target ) {
    const dp = []
    dp[target] = 0
    for (let i = target - 1; i >= 0; i--) {
        dp[i] = Infinity
        for (let j = 0; j < nums.length; j++) {
            dp[i] = Math.min((dp[i + nums[j]] ?? Infinity) + 1, dp[i])
        }
    }
    return dp[0] === Infinity ? -1 : dp[0]
};