322 零钱兑换 -动态规划

   **题目描述:**
   给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
   **示例1:**
   输入: coins = [1, 2, 5], amount = 11
   输出: 3 
   解释: 11 = 5 + 5 + 1
   **示例2:**
   输入: coins = [2], amount = 3
   输出: -1
   **说明:你可以认为每种硬币的数量是无限的。**
   
   **解法一:贪心+dfs**
   参考解法(https://leetcode-cn.com/problems/coin-change/solution/322-by-ikaruga/)
   主要思路:
   (1)尽量优先选择数值更大的硬币(因此需要先对硬币金额进行排序);
   (2)为了更快的添加硬币,使用除法来求取一次能够最多添加多少个当前大面值的硬币,既k=amount/coins[index];
   (3)使用这个思路遍历所有的可能的组合,计算量会比较大,这里使用剪枝,既新添加的可能的组合的元素的个数,要小于当前的已经获得可行组合的元素的个数,既k+count<ans;
class Solution {
public:
    
    void helper(vector<int>& coins,int amount,int index,int count,int& ans){
        //两个if都是递归的终止条件
        //当amount为0,既表示找到了一个可行的解,递归终止
        if(amount==0){
            //更新ans,保持ans存储的是最少的元素的个数
            ans=min(ans,count);
            return;
        }
        //当遍历完所有的coins的元素时,递归终止
        if(index==coins.size())
            return;
        //k初始化为最大的可能的数量,但后面的不满足时,通过自减--k,来实现可能的新的组合
        //判断条件k>=0保证k的有效性,k+count<ans用于提前终止不需要递归搜索
        for(int k=amount/coins[index];k>=0&&k+count<ans;--k)
            helper(coins,amount-k*coins[index],index+1,count+k,ans);
    }
    
    int coinChange(vector<int>& coins, int amount) {
         //处理amount为0的特殊情形
        if(amount==0)
            return 0;
         //将硬币进行排序,使用反向迭代器,获得降序数组
        sort(coins.rbegin(),coins.rend());
        //初始化存储结果的变量
        int ans=INT_MAX;
        //辅助的搜索函数,深度搜索
        helper(coins,amount,0,0,ans);
        //返回结果,根据ans是否改变,返回结果
        return ans==INT_MAX?-1:ans;
    }
};

解法2 动态规划,自底向上

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        //初始化dp数组,个数使用要组成的金额数加1,保证各个可能的金额的遍历
        //每个元素初值同样使用金额数加1,这个数量的硬币数量数不可能获得,因为硬币面值最小为1,则组成金额amount,最多只有amount个硬币
        vector<int> dp(amount+1,amount+1);
        dp[0]=0;
        
        //遍历dp的各个元素,保证每个dp元素,是其索引对应的金额下,所对应的硬币的最少的可能组成
        for(int i=1;i<=amount;++i){
            //对于当前的i,既金额,遍历每一个可能的硬币,既遍历coins数组的每一个元素
            for(int j=0;j<coins.size();++j){
                //保证当前的硬币的面值,要小于需要组成的金额值,这样才能放入可能的组成中
                if(coins[j]<=i)
                    //更新dp元素值,保证dp值最小
                    dp[i]=min(dp[i],dp[i-coins[j]]+1);
            }
        }
        //根据dp最后的值是否有更新,既金额amount是否有组成方式,返回结果
        return dp[amount]>amount?-1:dp[amount];
    }
};

解法3 动态规划,自顶向下

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值