322. 零钱兑换#
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
基本是按照这篇题解的思路
贪心
11. 想要总硬币数最少,肯定是优先用大面值硬币,所以对 coins 按从大到小排序
12. 先丢大硬币,再丢会超过总额时,就可以递归下一层丢的是稍小面值的硬币
乘法对加法的加速
21. 优先丢大硬币进去尝试,也没必要一个一个丢,可以用乘法算一下最多能丢几个
k = amount / coins[c_index] 计算最大能投几个
amount - k * coins[c_index] 减去扔了 k 个硬币
count + k 加 k 个硬币
如果因为丢多了导致最后无法凑出总额,再回溯减少大硬币数量
最先找到的并不是最优解
31. 注意不是现实中发行的硬币,面值组合规划合理,会有奇葩情况
32. 考虑到有 [1,7,10] 这种用例,按照贪心思路 10 + 1 + 1 + 1 + 1 会比 7 + 7 更早找到
33. 所以还是需要把所有情况都递归完
ans 疯狂剪枝
41. 贪心虽然得不到最优解,但也不是没用的
42. 我们快速算出一个贪心的 ans 之后,虽然还会有奇葩情况,但是绝大部分普通情况就可以疯狂剪枝了
作者:ikaruga
链接:https://leetcode-cn.com/problems/coin-change/solution/322-by-ikaruga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
private int min=Integer.MAX_VALUE;
public int coinChange(int[] coins, int amount) {
if(coins==null||coins.length==0)
return -1;
Arrays.sort(coins);
helper(coins,0,coins.length-1,amount);
return min==Integer.MAX_VALUE?-1:min;
}
public void helper(int[] coins,int res,int index,int all)
{
if(all==0)
{
min=Math.min(res,min);
return ;
}
if(index<0) //说明这种移动不能得到一个有效值,所以直接返回。
return;
for(int i=all/coins[index];i>=0&&res+i<min;i--) //这里i取到0就会跳到下一个位置,就可以在最上层实现移动。
{
helper(coins,res+i,index-1,all-i*coins[index]);
}
}
}
使用dp来做,让amount从小到大增加,使得后面的dp值复用前面的
class Solution {
public int coinChange(int[] coins, int amount) {
if(coins==null||coins.length==0)
return -1;
int[] dp=new int[amount+1];
for(int i=1;i<dp.length;i++)
dp[i]=Integer.MAX_VALUE;
for(int i=1;i<=amount;i++)
{
int min=Integer.MAX_VALUE;
for(int j=0;j<coins.length;j++)
{
if(i-coins[j]>=0&&(dp[i-coins[j]]!=Integer.MAX_VALUE))
min=Math.min(dp[i-coins[j]]+1,min);
}
dp[i]=min;
}
return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];
}
}