[LeetCode] Coin Change 2 硬币找零之二

简介:

You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Note: You can assume that

  • 0 <= amount <= 5000
  • 1 <= coin <= 5000
  • the number of coins is less than 500
  • the answer is guaranteed to fit into signed 32-bit integer 

Example 1:

Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1 

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2. 

Example 3:

Input: amount = 10, coins = [10] 
Output: 1

这道题是之前那道Coin Change的拓展,那道题问我们最少能用多少个硬币组成给定的钱数,而这道题问的是组成给定钱数总共有多少种不同的方法。那么我们还是要使用DP来做,首先我们来考虑最简单的情况,如果只有一个硬币的话,那么给定钱数的组成方式就最多有1种,就看此钱数能否整除该硬币值。那么当有两个硬币的话,那么组成某个钱数的方式就可能有多种,比如可能由每种硬币单独来组成,或者是两种硬币同时来组成。那么我们怎么量化呢,比如我们有两个硬币[1,2],钱数为5,那么钱数的5的组成方法是可以看作两部分组成,一种是由硬币1单独组成,另一种是此时我们如果取出一个硬币2,相当于由硬币[1,2]组成的钱数为3的总方法。是不是不太好理解,多想想。那么我们的需要一个二维的dp数组,其中dp[i][j]表示用前i个硬币组成钱数为j的不同组合方法,那么我们的递推公式也在上面的分析中得到了:

dp[i][j] = dp[i - 1][j] + (j >= coins[i - 1] ? dp[i][j - coins[i - 1]] : 0)

注意我们要初始化每行的第一个位置为0,参见代码如下:      

解法一:

public:
    int change(int amount, vector<int>& coins) {
        vector<vector<int>> dp(coins.size() + 1, vector<int>(amount + 1, 0));
        dp[0][0] = 1;
        for (int i = 1; i <= coins.size(); ++i) {
            dp[i][0] = 1;
            for (int j = 1; j <= amount; ++j) {
                dp[i][j] = dp[i - 1][j] + (j >= coins[i - 1] ? dp[i][j - coins[i - 1]] : 0);
            }
        }
        return dp[coins.size()][amount];
    }
};
 

我们可以对空间进行优化,我们发现dp[i][j]仅仅依赖于dp[i - 1][j] 和 dp[i][j - coins[i - 1]] 这两项,那么我们可以使用一个一维dp数组来代替,此时的dp[i]表示组成钱数i的不同方法。其实最开始的时候,博主就想着用一维的dp数组来写,但是博主开始想的方法是把里面两个for循环调换了一个位置,结果计算的种类数要大于正确答案,所以一定要注意for循环的顺序不能搞反,参见代码如下:

解法二:

public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount + 1, 0);
        dp[0] = 1;
        for (int coin : coins) {
            for (int i = coin; i <= amount; ++i) {
                dp[i] += dp[i - coin];
            }
        }
        return dp[amount];
    }
};
在CareerCup中,有一道极其相似的题9.8 Represent N Cents 美分的组成,书里面用的是那种递归的方法,博主想将其解法直接搬到这道题里,但是失败了,博主发现使用那种的递归的解法必须要有值为1的硬币存在,这点无法在这道题里满足。你以为这样博主就没有办法了吗?当然有,博主加了判断,当用到最后一个硬币时,我们判断当前还剩点钱数是否能整除这个硬币,不能的话就返回0,否则返回1。还有就是用二维数组的memo会TLE,所以博主换成了map,就可以通过啦~

解法三:

public:
    int change(int amount, vector<int>& coins) {
        if (amount == 0) return 1;
        if (coins.empty()) return 0;
        map<pair<int, int>, int> memo;
        return helper(amount, coins, 0, memo);
    }
    int helper(int amount, vector<int>& coins, int idx, map<pair<int, int>, int>& memo) {
        if (amount == 0) return 1;
        else if (idx >= coins.size()) return 0;
        else if (idx == coins.size() - 1) return amount % coins[idx] == 0;
        if (memo.count({amount, idx})) return memo[{amount, idx}];
        int val = coins[idx], res = 0;
        for (int i = 0; i * val <= amount; ++i) {
            int rem = amount - i * val;
            res += helper(rem, coins, idx + 1, memo);
        }
        return memo[{amount, idx}] = res;
    }
};

参考资料:

https://discuss.leetcode.com/topic/89238/golang-recursive-solution

https://discuss.leetcode.com/topic/79071/knapsack-problem-java-solution-with-thinking-process-o-nm-time-and-o-m-space

本文转自博客园Grandyang的博客,原文链接:[LeetCode] Coin Change 2 硬币找零之二

,如需转载请自行联系原博主。

相关文章
LeetCode 322. Coin Change
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
87 0
LeetCode 322. Coin Change
|
Java Python
Leetcode-Medium 322. Coin Change
Leetcode-Medium 322. Coin Change
100 0
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
58 6
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
52 4
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
118 2
|
28天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
22 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
4月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
64 7