首先尝试暴力破解方法(暴力破解加记忆化,减少重复计算,否则会超时):

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        if(amount < 1) return 0;
        count.resize(amount);
        return helper(coins,amount);
    }
private:
    vector<int> count;
    int helper(vector<int>& coins,int amount)
    {
        if(amount < 0) return -1;
        if(amount == 0) return 0;
        
        if(count[amount - 1] != 0) return count[amount - 1];
        int ans = INT_MAX;
        for(int i = 0;i < coins.size();i++)
        {
            int res = helper(coins,amount - coins[i]);
            if(res >= 0 && res < ans)
                ans = res + 1;
           
        }
        count[amount - 1] = ans == INT_MAX ? -1 : ans;
        return count[amount - 1];
    }
};

找零钱问题在使用动态规划DP算法时可以当成爬楼梯问题,例如我要兑换面值为1000,零钱有10,20,50 ,100,那么类比成爬楼梯为,如果要爬到1000阶楼梯,一步能走10,20,50,100(邮电脱离实际),那么走到第1000阶最小步数,就是最后一步走10 ,20,50,100步时前面所走的最小步数(上面代码内层循环)。
最后的返回值判断是否找到兑换零钱方案,如果没有兑换方案,则保持默认值amount + 1,当然这种方法存在缺陷,当amount为INT_MAX时存在溢出,之所以没有报错是因为题目有限制amount最大值为10000.

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 < coins.size();j++)
            {
                if(coins[j] <= i)
                    dp[i] = min(dp[i],dp[i - coins[j]] + 1);
            }
        }
        
        return dp[amount] > amount ? -1:dp[amount];
    }
};

力扣官方解法