Leetcode 322. 零钱兑换【动态规划&贪心算法+回溯】

文章目录

  • 问题描述
  • 解题报告
    • 动态规划
    • 贪心+回溯
  • 实现代码
    • 动态规划实现
    • 贪心+回溯实现
  • 参考资料

问题描述

给定不同面额的硬币 c o i n s coins coins 和一个总金额 a m o u n t amount amount。编写一个函数来计算可以凑成总金额所需的最小的硬币个数、如果没有任何一种硬币组合能组成总金额,返回 − 1 -1 1
输入: c o i n s = [ 1 , 2 , 5 ] , a m o u n t = 11 coins=[1, 2, 5], amount=11 coins=[1,2,5],amount=11
输出: 3 3 3
解释: 11 = 5 + 5 + 1 11=5+5+1 11=5+5+1
每种硬币的数量是无限的

解题报告

动态规划

d p [ i ] dp[i] dp[i] 表示组合成面额为 i i i 的硬币需要的硬币个数,则:

d p [ i ] = m i n ( d p [ i ] , d p [ i − c o i n [ j ] ] + 1 )        j 取 值 [ 0 , 1 , ⋯   , n ] dp[i]=min(dp[i], dp[i-coin[j]]+1) \;\;\;j 取值[0, 1, \cdots, n] dp[i]=min(dp[i],dp[icoin[j]]+1)j[0,1,,n]

贪心+回溯

这种实现方式源自题解区:lkaruga
贪心思想
为了使得总硬币数目最少,优先使用大面值的硬币,所以对 c o i n s coins coins 进行逆序排序;
回溯思想
如果首先将大面值的硬币使用了,则可能无法凑出目标值,所以需要进行回溯,依次减少大面值的硬币数量

注意:最先找到不一定是最优解,所以其他情况我们也需要考虑。考虑其他情况时,合理使用剪枝,可以减少时间消耗。

实现代码

动态规划实现

int coinChange(vector<int>&coins,int amount){
     
        vector<int>dp(amount+1,amount+1);
        dp[0]=0;
        for(int i=1;i<=amount;i++){
     
            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]==amount+1)?-1:dp[amount];
    }
};

贪心+回溯实现

class Solution{
     
    public:
        int coinChange(vector<int>&coins,int amount){
     
            sort(coins.begin(), coins.end(),greater<int>());
            int ans=INT_MAX;
            dfs(coins, amount, 0, 0, ans);
            return ans==INT_MAX?-1:ans;
        }
        void dfs(vector<int>&coins,int amount, int i, int count,int& ans){
     
            if(amount==0){
     
                ans=min(ans, count);
                return;
            }
            if(i==coins.size()) return;
            for(int k=amount/coins[i];k>=0&&k+count<ans;k--){
     
                dfs(coins, amount-k*coins[i], i+1, count+k,ans);
            }
        }
};

参考资料

[1] Leetcode 322. 零钱兑换
[2] 题解区:lkaruga

你可能感兴趣的:(leetcode,贪心,动态规划)