换硬币
题目描述
也是一道非常经典的题目。给定一个数组 coins
,里面的每个数字代表可以使用的硬币面值,假设硬币的个数是无穷的。需要将一个大的数值,用尽可能少的硬币数目替换,使得它们面值相等。如果不存在这样的替换,返回 -1
。
Example 1:
Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
解题方法
方法一:动态规划
动态规划。原始的思想是,使用 dp[i][j]
代表使用了第 i 个类型的硬币以后,能表示面值 j 的最小的硬币数目。假如 i+1
个硬币的面值是 c
,更新的方式是 dp[i+1][j] = min(dp[i][j], dp[i][j - c] + 1)
。因为这里 i+1
轮的所有结果,都只跟 i
轮的相关,所以可以只使用一个一维的数组,来降低空间复杂度。
总结来说,可以按如下进行计算:
- 一开始初始化空间大小为
amount + 1
的数组空间,dp[0]
置为0
,其余置为无穷。 - 按照
dp[i] = min(dp[i], dp[i - c] + 1)
更新 - 返回位置
amount
处的值
代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int c : coins){
for (int i = c; i <= amount; i ++){
dp[i] = min(dp[i], dp[i - c] + 1);
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
};
方法二:深度优先搜索
在空间中不断进行深度优先搜索。这里需要注意,如果不进行剪枝,会多进行很多不必要的运算。比如先搜索面值为 1
的硬币,会有很大的搜索空间,如果我们先搜索面值比较大的硬币,然后得到了一个解,如果我们接下来要搜索的时候,硬币数目已经大于了当前的解,那么就没必要进行搜索了。这样剪枝会带来很大的效率的提升。
方法二代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
// 先使用最大的
sort(coins.rbegin(), coins.rend());
int ans = INT_MAX;
core(coins, amount, 0, 0, ans);
return ans == INT_MAX ? -1 : ans;
}
private:
void core(vector<int> coins, int amount, int current_using, int current_count, int& ans){
int coin = coins[current_using];
if (current_using == coins.size() - 1){
if (amount % coin == 0){
ans = min(ans, current_count + amount / coin);
}
}
else{
// 先尽可能多的使用最大的
// 在递归的时候进行剪枝,如果不能使得结果变得更好,就不要继续下去
for (int k = amount / coin;
k >= 0 && k + current_count < ans; k --){
core(coins, amount - k * coin, current_using + 1, current_count + k, ans);
}
}
}
};