2019-11-23 15:45:42 +00:00
|
|
|
|
# [322. Coin Change](https://leetcode.com/problems/coin-change/)
|
|
|
|
|
|
|
|
|
|
# 思路
|
|
|
|
|
换零钱,给定一个钱的价值amount和一些面值,假设每个面值的硬币数都是无限的,问我们最少能用几个硬币组成给定的价值。
|
|
|
|
|
|
|
|
|
|
## 思路一
|
|
|
|
|
此题很容易想到直接搜索所有可能然后保持一个全局的结果res,但是直接搜索很容易超时。因此需要做一些剪枝,为了剪枝剪得更狠,我们可以先对面值数组coins从大到小
|
|
|
|
|
排个序,这样做的原因是如果某个amount可以直接被某个面值整除,那就可以直接返回整除的结果而不用考虑比这个面值小的面值了。
|
|
|
|
|
|
|
|
|
|
我们定义一个搜索函数helper,传入排好序的coins、面值开始索引start(初始为0)、当前目标target(初始为amount)、当前已用硬币数(初始为0)、全局结果res(初始为INT_mAX,引用传入):
|
|
|
|
|
* 当start超出coins数组的范围,直接返回即可;
|
|
|
|
|
* 当target可以被 coins[start] 整除,那么全局res就更新为`min(res, cur + target / coins[start])`然后返回。
|
|
|
|
|
* 否则就需要进行递归搜索了,此时我们需要采取贪心的策略,即从最多可加入硬币coins[start]的数目`target / coins[start]`开始逐渐递减(代码中这个次数为i),
|
|
|
|
|
一旦遇到`cur + i >= res - 1`直接break,因为当前的i递归下去最少需要的硬币数`cur + i + 1`不会比res小,所以就不用进行搜索了;而再循环下去(即i--)res也不可能比当前res小(想想为什么),因此也不用再循环下去了。
|
|
|
|
|
这个剪枝很关键,少了这个剪枝就会超时,加上这个剪枝直接击败99%。若不满足这个剪枝条件,那么递归下去就是。
|
|
|
|
|
|
|
|
|
|
## 思路二
|
2019-11-26 12:49:17 +00:00
|
|
|
|
熟悉动态规划的童鞋一眼就可以看出这其实就是个背包问题,具体来说是一个恰好装满的完全背包问题,
|
|
|
|
|
我在我的博客文章[动态规划之背包问题系列](https://tangshusen.me/2019/11/24/knapsack-problem/)中对常见的几类背包问题做了个总结,此题的分析见5.2节,这里只给出代码。
|
2019-11-23 15:45:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 思路
|
|
|
|
|
## 思路一
|
|
|
|
|
``` C++
|
|
|
|
|
class Solution {
|
|
|
|
|
public:
|
|
|
|
|
int coinChange(vector<int>& coins, int amount) {
|
|
|
|
|
int res = INT_MAX, n = coins.size();
|
|
|
|
|
sort(coins.begin(), coins.end(), greater<int>());
|
|
|
|
|
helper(coins, 0, amount, 0, res);
|
|
|
|
|
return (res == INT_MAX) ? -1 : res;
|
|
|
|
|
}
|
|
|
|
|
void helper(vector<int>& coins, int start, int target, int cur, int& res) {
|
|
|
|
|
if (start >= coins.size()) return;
|
|
|
|
|
if (target % coins[start] == 0){
|
|
|
|
|
// 能整除的话肯定不用再递归了, 因为coins已从大到小排好序
|
|
|
|
|
res = min(res, cur + target / coins[start]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (int i = target / coins[start]; i >= 0; --i) {
|
|
|
|
|
// 满足这个条件就不用搜索也不用循环了
|
|
|
|
|
if (cur + i >= res - 1) break;
|
|
|
|
|
helper(coins, start + 1, target - i * coins[start], cur + i, res);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 思路二
|
2019-11-26 12:49:17 +00:00
|
|
|
|
``` C++
|
|
|
|
|
class Solution {
|
|
|
|
|
public:
|
|
|
|
|
int coinChange(vector<int>& coins, int amount) {
|
|
|
|
|
vector<int>dp(amount + 1, INT_MAX);
|
|
|
|
|
dp[0] = 0;
|
|
|
|
|
|
|
|
|
|
for(int i = 1; i <= coins.size(); i++)
|
|
|
|
|
for(int j = coins[i-1]; j <= amount; j++){
|
|
|
|
|
// 下行代码会在 1+INT_MAX 时溢出
|
|
|
|
|
// dp[j] = min(dp[j], 1 + dp[j - coins[i-1]]);
|
|
|
|
|
if(dp[j] - 1 > dp[j - coins[i-1]])
|
|
|
|
|
dp[j] = 1 + dp[j - coins[i-1]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dp[amount] == INT_MAX ? -1 : dp[amount];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|