题目的链接如下:https://leetcode-cn.com/problems/coin-change/
----------------------------------------------------------------------------------------------------------------------------
题目:
给定不同面额的硬币 coins 和一个总金额 amount。
编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
如果没有任何一种硬币组合能组成总金额,返回 -1
。
示例 1:
输入:coins=[1, 2, 5], amount = 11
输出:3
解释: 11 = 5 + 1 + 1
示例 2:
输入:coins=2, amount = 3
输入:-1
----------------------------------------------------------------------------------------------------------------------------
思路1:
首先排除贪心算法,因为如果是贪心算法,这道题的难度不会是中等(滑稽)。
简单思考一下,如果是贪心算法的话,如:coins = 1,8,9,amount=16。
贪心算法结果为:9+1+1+1+1+1+1+1 = 16,输出8,但是实际:8+8=16,输出2。
然后由此想到穷举法 -> 再想到递归,这样一来,这道题肯定是有思路解决了(几乎所有问题都是可以穷举的),但是,这样一来就会消耗很多资源,在leetcode提交是肯定会超时的。
思路2:
这道题和背包问题是一样的,就拿示例1来看,我们可以把amount=11,拆分成amount=0,1,2,3....来一步一步求解。
假设结果为x
- 首先amount=0的时候,输出肯定是0,即有0种组合。(x=0)
- amount=1的时候,可以遍历3枚硬币,判断是否拿这一枚
- 如果拿硬币1,那么amount=1-1=0,然后看当amount=0的时候,它的最优解是多少,由上面可得,amount=0的时候,最优解是0,所以现在的最优解是1+0=1,有1种组合(x=1)
- 如果拿硬币2,那么amount=1-2=-1,不满足条件
- 如果拿硬币5,那么amount=1-5=-4,不满足条件
- 得出amount=1的时候,最优解为1。(即x=1)
- amount=2的时候,遍历3枚硬币。
- 如果拿硬币1,那么amount=2-1=1,然后看当amount=1的时候,它的最优解是多少,由上面可得,amount=1的时候,最优解是1,所以现在的最优解是1+1=2,有2种组合。(最优解:x=2)
- 如果拿硬币2,那么amount=2-2=0,然后看当amount=2的时候,它的最优解是多少,由上面可得,amount=0的时候,最优解是0,所以现在的最优解是1+0=1,有1种组合。(最优解:x=1)
- 如果拿硬币5,那么amount=2-5=-3,不满足条件
- 得出amount=2的时候,最优解为1。(即x=1)。(1<2,肯定取小的那个啦)
amount=3的时候,遍历3枚硬币。
- 如果拿硬币1,那么amount=3-1=2,然后看当amount=2的时候,它的最优解是多少,由上面可得,amount=2的时候,最优解是1,所以现在的最优解是1+1=2,有2种组合。(最优解:x=2)
- 如果拿硬币2,那么amount=3-2=1,然后看当amount=1的时候,它的最优解是多少,由上面可得,amount=1的时候,最优解是1,所以现在的最优解是1+1=2,有1种组合。(最优解:x=2)
- 如果拿硬币5,那么amount=3-5=-2,不满足条件
- 得出amount=3的时候,最优解为2。(即x=2)。
- amount=4的时候,遍历3枚硬币。
- 如果拿硬币1,那么amount=4-1=3,然后看当amount=3的时候,它的最优解是多少,由上面可得,amount=3的时候,最优解是2,所以现在的最优解是1+2=3,有3种组合。(最优解:x=3)
- 如果拿硬币2,那么amount=4-2=2,然后看当amount=2的时候,它的最优解是多少,由上面可得,amount=2的时候,最优解是1,所以现在的最优解是1+1=2,有2种组合。(最优解:x=2)
- 如果拿硬币5,那么amount=4-5=-1,不满足条件
- 得出amount=4的时候,最优解为2。(即x=2)。(因为2<3)
...
...
...
(剩下的就不分析了,大家可以自己拿草稿纸演算一下,如果对背包问题比较熟悉的同学,思路应该很清晰了,如果不熟悉,建议去看一下背包问题,那个是非常经典的一个题目)
附上代码:(直接从LeetCode的编辑框粘贴过来的)
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
if (amount < 0 || coins.size() == 0) return -1;
if (amount == 0) return 0;
int memory[amount + 1]; // 定义一个数组,用来存放每一步的最优解
memory[0] = 0; // 初始化,因为amount=0的时候,必定为0
for (int i=1; i<=amount; ++i) {
// 定义一个临时变量,用于存放每一步amount,在遍历硬币数组时候的最小值,即暂存每一步的最优解
int min = INT_MAX;
for (int j=0; j<coins.size(); ++j) { // 遍历所有硬币的组合
if (i - coins[j] >= 0 && memory[i-coins[j]] < min) {
// 自底向上搜索,如果还剩i元,在减去 coins[j]元的时候,即以前的“经验值”,数量小于当前的最小值
min = memory[i-coins[j]] + 1;
}
}
memory[i] = min;
}
return memory[amount] == INT_MAX ? -1 : memory[amount];
}
};