概述
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
典型的DP问题,自下而上和自上而下都可以解决,使用DFS配合恰当的减枝策略也效果拔群。
具体思路讲解:题解
嘴一句:如果换成求组合的个数则直接转换为数论中无线多重集的组合个数的问题,直接套公式即可。
DFS
class Solution {
public:
int ans = INT_MAX;
int coinChange(vector<int>& coins, int amount) {
sort(coins.begin(), coins.end());
dfs(coins, coins.size() - 1, amount, 0);
return ans == INT_MAX ? -1 : ans;
}
void dfs(vector<int>& coins, int index, int amount, int cnt)
{
if (index < 0){
return;
}
for (int c = amount / coins[index]; c >= 0; c--){ //大面值从多到少
int remains = amount - c * coins[index];
int tempcnt = cnt + c;
if (remains == 0){ //已经完成了一种分配方案
this->ans = min(this->ans, tempcnt);
break;
}
if (tempcnt >= ans){
break;
}
dfs(coins, index - 1, remains, tempcnt);
}
}
};
自上而下DP
自上而下的DP一般会造成递归调用中的重复计算,所以一般需要加辅助内存来保存计算结果,如下面的代码中使用了一个辅助vector作为记忆数组。
class Solution {
public:
vector<int> count; //存储组成金额S所需的最少硬币数量。
int dp(vector<int> &coins, int rem)
{
if (rem < 0) return -1;
if (rem == 0) return 0;
//动态规划记忆数组;
if (count[rem - 1] != 0) return count[rem - 1];
int Min = INT_MAX;
for (int coin : coins){
int res = dp(coins, rem - coin);
if (res >= 0 && res < Min){
Min = res + 1;
}
}
count[rem - 1] = Min == INT_MAX ? -1 : Min;
return count[rem - 1];
}
int coinChange(vector<int>& coins, int amount) {
if (amount < 1) return 0;
count.resize(amount);
return dp(coins, amount);
}
};
自下而上DP
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int Max = amount + 1;
vector<int> dp(amount + 1, Max);
dp[0] = 0;
for (int i = 1; i <= amount; ++i){
for (int j = 0; j < (int)coins.size(); ++j){
if (coins[j] <= i){
dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
}
}
//因为是用Max初始化的,所以如果大于amount就说明没有改变,无法组成指定的金额。
return dp[amount] > amount ? -1 : dp[amount];
}
};
最后
以上就是畅快黄豆为你收集整理的每日一题:零钱兑换的全部内容,希望文章能够帮你解决每日一题:零钱兑换所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复