LeetCode 322. 零钱兑换:

147 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

322. 零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

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

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

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

链接:leetcode-cn.com/problems/co…

思路:

利用dp:类似于LeetCode70:爬楼梯(唯一区别就是爬楼梯时:比如爬3层楼梯时,可能情况有1,1,1、1,2、2,1这三种,需要考虑顺序不同的情况。但是在零钱兑换中1,2和2,1属于同一种情况,不考虑顺序不同的情况)。此外,这里可以理解为:每次爬1,2,5阶楼梯层数~

i:表示当前要凑的金额

dp[i]:表示凑总金额为i时,所需的最少的硬币个数

硬币种类有三种:1、2、5元

(1)凑够的总金额为11时,需要找到总金额为10元(11-1)、9元(11-2)、6元(11-5)的情况

(2)凑够的总金额为10时,需要找到总金额为9元(10-1)、8元(10-2)、5元(10-5)的情况

(3)...

(4)一直到总金额为0元( 图示) 时停止,此时表示存在正好凑够amount(比如11)的总金额的组合

(5)找出其中所需的最少的硬币个数的组合,即可~

补充:

可使用BFS广度优先遍历求解:最先发现状态树中出现面额为0的解为最优解,其树的层数就是组合所需的最少的硬币个数

​编辑

时间复杂度: O(m*n)  其中 m 是金额,n 是面额数。我们一共需要计算 O(m) 个状态,m 为题目所给的总金额

空间复杂度: O(m)     开辟长度为m的数组(如示例中的amount = 11)

// 类似于LeetCode70:爬楼梯,每次爬1,2,5阶楼梯层数
int coinChange(vector<int>& coins, int amount) {
    int Max = amount + 1;
    vector<int> dp(amount + 1, Max);            // dp下标: 需凑够的总金额(如1~11);dp[i]: i=amount时组合使用的硬币数
    dp[0] = 0;                                  // 占位用,i下标从0开始(没有面额为0的硬币种类)
    for (int i = 1; i <= amount; i++)           // i:表示要凑的面值数:先找到amount为1的情况,进而求解amount=2...3...题目给定amount...
    {
        for (int j = 0; j < coins.size(); j++)  // 可能的面额:每次找出可能使用到的不同硬币情况
        {
            if (i >= coins[j])                  // 如果当前遍历到的金额i(1~11)大于当前面额(coins[j]),则计算个数 
            {
                // 状态方程:   
                dp[i] = min(dp[i], dp[i - coins[j]] + 1); // +1:coins[j]小于当前累计到达的金额数,可以使用该面额硬币(coins[j]),硬币个数+1
            }
        }
    }
    return dp[amount] > amount ? -1 : dp[amount]; // 因为开始时dp数组中所有值初始化为amount + 1,都大于amount;当状态数组dp[amount]也就是目标金额dp[11]不等于初始值时(说明初始值循环中被改动过),所以可以凑够dp[11]
    // return dp[amount] == amount + 1 ? -1 : dp[amount];   // 或者:dp[amount] = amount + 1(初始值)时,说明初始值循环中没被改动过,所以无法凑够总金额amount