Blog of CodeJELLOW’S ENGINEERING NOTES
← 返回文章

零钱兑换-DP

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

本文目录 ⌄

    问题描述

    给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

    示例 1:

    输入: coins = [1, 2, 5], amount = 11
    输出: 3
    解释: 11 = 5 + 5 + 1
    示例 2:

    输入: coins = [2], amount = 3
    输出: -1
     

    说明:
    你可以认为每种硬币的数量是无限的。

    题解

    寻找最优解的问题,很可能和动态规划有关。尤其是这种当前问题的最优解可能分解为子问题,比如求11可分解的零钱的最小个数相当于求(11-任意面值)+1的最小个数。以此类推。

    算法:

    我们采用自下而上的方式进行思考。仍定义 F(i)F(i) 为组成金额 ii 所需最少的硬币数量,假设在计算 F(i)F(i) 之前,我们已经计算出 F(0)-F(i-1)F(0)−F(i−1) 的答案。 则 F(i)F(i) 对应的转移方程应为

    例子1:假设

    coins = [1, 2, 5], amount = 11
    则,当 i==0时无法用硬币组成,为 0 。当 i<0 时,忽略 F(i)

    F(i) 最小硬币数量
    F(0) 0 //金额为0不能由硬币组成
    F(1)=min(F(1-1),F(1-2),F(1-5))+1=1
    F(2)=min(F(2-1),F(2-2),F(2-5))+1=1
    F(3)=min(F(3-1),F(3-2),F(3-5))+1=2
    F(4)=min(F(4-1),F(4-2),F(4-5))+1=2
    … …
    F(11)=min(F(11-1),F(11-2),F(11-5))+1=3

    代码

    TEXT
    /*
        1.动态规划,当前问题的最优解可由子问题得到,
        2.dp[i]=min(dp[i-s1]......dp[i-sn])+1
    */
    class Solution {
    public:
        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(coins[j]<=i){
                        dp[i]=min(dp[i],dp[i-coins[j]]+1);
                    }
                }
            }
            return dp[amount]>amount?-1:dp[amount];
        }
    };

    时间复杂度:O(Sn),其中 S 是金额,n 是面额数。我们一共需要计算 O(S) 个状态,S 为题目所给的总金额。对于每个状态,每次需要枚举 n 个面额来转移状态,所以一共需要 O(Sn) 的时间复杂度。

    通过 RSS 订阅更新 →