LeetCode 322:零钱兑换(动态规划)
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。
dp解法:
**思路:**dp[N] = MIN{dp[N-coins[0], dp[N-coins[1]],…dp[N-coins[N]] }+1;
int coinChange(vector<int>& coins, int amount) {
long dp[amount+1] ={INT_MAX};
dp[0] = 0;
for(int i = 1; i < amount + 1 ; i++) {
dp[i] = INT_MAX;
for(int j = 0; j < coins.size(); j++) {
if(i >= coins[j]) {
dp[i] = min(dp[i], (dp[i - coins[j]] + 1));
}
}
}
return dp[amount] == INT_MAX ? -1 : dp[amount] ;
}
另一种更快的解法
先将硬币面额从大到小排序, 再从最大面额的个数开始分支。即先考虑用n个最大面额硬币, 剩余金额在用剩余面额硬币兑换; 再考虑用n-1个最大面额硬币, 剩余金额在用剩余面额硬币兑换…依次类推。再进行剪枝, 若已用数量已大于最小数量, 则无续再往下讨论。
int coinChange(vector<int>& coins, int amount) {
if (amount == 0) return 0;
sort(coins.rbegin(), coins.rend());
int miniAns = INT_MAX;
coinChangeIter(coins, amount, 0, 0, miniAns);
miniAns = (miniAns == INT_MAX) ? -1 : miniAns;
return miniAns;
}
void coinChangeIter(vector<int>& coins, int amount, int index, int count, int& miniAns){//amount: 目标值 coins:硬币数组 index: 有效硬币数组范围 count: 已使用硬币数 miniAns: 当前最小值
if (amount == 0){ //遍历到最后,有解
miniAns = count < miniAns ? count : miniAns;
return;
}
if (index == coins.size())
return; //遍历到最后,无解
for (int maxCoinNum = amount / coins[index]; maxCoinNum >= 0 && maxCoinNum + count < miniAns; maxCoinNum--){
coinChangeIter(coins, amount - maxCoinNum * coins[index], index + 1, count + maxCoinNum, miniAns);
}
}
自己的再版(31% 18%)
int coinChange(vector<int>& coins, int amount) {
if (amount == 0) return 0;
sort(coins.begin(), coins.end());
if (amount < coins[0]) return -1;
vector<int> res(amount + 1, INT_MAX); res[0] = 0;
for (int i = coins[0]; i < amount + 1; i++) {
int minvalue = res[i - coins[0]];
for (int j = 0; j < coins.size(); j++) {
if (i - coins[j] >= 0 && res[i - coins[j]] < minvalue)
minvalue = res[i - coins[j]];
}
if (minvalue != INT_MAX)
res[i] = 1 + minvalue;
}
return res[amount] == INT_MAX ? -1 : res[amount];
}
自己的初版(时空复杂度垫底5%,5%)
int coinChange(vector<int>& coins, int amount) {
if(amount==0) return 0;
sort(coins.begin(), coins.end());
if (amount < coins[0]) return -1;
vector<int> res(amount + 1, INT_MAX); res[0] = 0;
for (int i = coins[0]; i < amount + 1; i++) {
vector<int> findmin;
for (int j = 0; j < coins.size(); j++) {
if (i - coins[j] >= 0) findmin.push_back(res[i - coins[j]]);
}
if (findmin.size() != 0) {
int minvalue = *min_element(findmin.begin(), findmin.end());
if (minvalue != INT_MAX)
res[i] = 1 + minvalue;
}
}
return res[amount] == INT_MAX ? -1 : res[amount];
}