零钱兑换

535 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

一、题目

leetcode 零钱兑换

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

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

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

示例 1:

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

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

二、题解

题意 需要使用不同面值的硬币来组成目标金额,求使用的最小硬币数量,并且可以任务每种硬币数量无限。

方法一 简单的可以枚举所有的硬币组成方案,然后选出最优的方案,但是时间复杂度可能比较高会超时,所以可以利用动态规划,用一个长度为amount + 1的数组记录每种金额下所需的最少硬币数;定义dp数组,那么dp[i]就表示金额i所需的最少硬币数多少,那么dp[amount]就是题目所求答案。具体的首先要初始化dp数组,对于dp[0]的状态下肯定就是0了,对于其他的金额,直接初始化一个最大值。然后循环枚举dp[i]的硬币个数的组合,然后循环数组coins的硬币,找出组合的硬币,如果当前金额i对于硬币的面值时,就用当前i的金额减去当前硬币减去当前硬币面值再加上一个硬币就是组合硬币所需总数,然后要去最小的所以为dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)。遍历结束之后,如果dp[amount]是大于初始化dp的值时,就是无法组合成金额amount直接返回-1,否则返回dp[amount]。最后可以提前判断一下,如果金额amount是0的话就直接返回0了。

三、代码

方法一 Java代码

class Solution {
    public int coinChange(int[] coins, int amount) {
        if (amount == 0) {
            return 0;
        }
        int max = amount + 1;
        int[] dp = new int[amount + 1];
        dp[0] = 0;
        for (int i = 1; i < dp.length; i++) {
            dp[i] = max;
        }
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (coins[j] <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

时间复杂度:O(n^2),双重循环枚举每种金额所需的最小硬币数。

空间复杂度:O(n),dp数组记录每种金额的最小硬币数。