牛客热题:兑换零钱(一)

📟作者主页:慢热的陕西人

🌴专栏链接:力扣刷题日记

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

在这里插入图片描述

牛客热题:兑换零钱(一)

题目链接

兑换零钱(一)_牛客题霸_牛客网 (nowcoder.com)

方法一:动态规划

思路

首先我们思考当arr中最小的零钱是1时,那么这时候是凑成aim的最大钱数,aim个1块钱。

①状态表示:

d p [ i ] dp[i] dp[i]表示的是i块钱用数组中的零钱表示的最少钱数。

②状态转化:

​ 显然 d p [ i ] dp[i] dp[i]的值可以从 d p [ i − a r r [ j ] ] dp[i - arr[j]] dp[iarr[j]]的状态获取,但是题目要求最小值,所以取一个min

d p [ i ] = m i n ( d p [ i ] , d p [ i − a r r [ j ] ] + 1 ) dp[i] = min(dp[i], dp[i - arr[j]] + 1) dp[i]=min(dp[i],dp[iarr[j]]+1)

③填表顺序:

​ 对于dp数组的话,因为dp的当前状态需要用到之前状态,所以按照从左的到右的顺序

​ 对于原数组arr,这个数组的话不牵扯到状态转移,只需要遍历就好了,习惯上也从左到右来遍历

④初始化:

对于dp数组,开始时除了 d p [ 0 ] = 0 dp[0] = 0 dp[0]=0,其余的dp状态都应该设定为对应的最大值+1,也就是aim + 1;

⑤返回值:

代码

    int minMoney(vector<int>& arr, int aim) 
    {
        //创建dp数组
        vector<int> dp(aim + 1, aim + 1);
        dp[0] = 0;
        for(int i = 0; i <= aim; ++i)
        {
            for(int j = 0; j < arr.size(); ++j)
            {
                if(i >= arr[j])
                dp[i] = min(dp[i], dp[i - arr[j]] + 1);
            }
        }
        return dp[aim] > aim ? -1 : dp[aim];
    }

复杂度

时间复杂度: O ( a i m ∗ a r r . s i z e ( ) ) O(aim * arr.size()) O(aimarr.size()), 遍历dp数组的同时去遍历原数组。

空间复杂度: O ( a i m + 1 ) O(aim + 1) O(aim+1),额外使用了一个dp数组,长度是aim+1。

方法二:递归

思路

  1. recursion 函数

    • 这是一个递归函数,用于计算组合达到目标金额 aim 所需的最小硬币数量。
    • 参数包括 arr(硬币面值数组)、aim(目标金额)、dp(用于记录中间结果的数组)。
  2. 基础情况

    • 如果 aim < 0,表示当前的组合方式超过了目标金额,返回 -1
    • 如果 aim == 0,表示当前的组合方式正好等于目标金额,返回 0(硬币数量为0)。
    • 如果 dp[aim - 1] != 0,说明 aim 这个金额已经计算过了,直接返回 dp[aim - 1]
  3. 递归求解

    • 初始化 MinINT_MAX,用于记录当前情况下的最小硬币数量。
    • 遍历所有硬币面值 arr[i],对于每个面值,计算使用该面值时剩余金额 aim - arr[i] 的最小硬币数量 res
    • 如果 res >= 0(说明可以组合成功)且 res + 1 比当前 Min 小,则更新 Min
  4. 更新结果

    • dp[aim - 1] 设置为 Min,如果 Min 仍然是 INT_MAX,表示无法组合成 aim,返回 -1,否则返回 Min
  5. minMoney 函数

    • 这是主函数,初始化了 dp 数组并调用了 recursion 函数来求解最小硬币数量。
    • 如果 aim < 1,直接返回 0,因为小于等于 0 的金额不需要硬币。

总结思路:

  • 使用递归方法和动态规划思想,通过 dp 数组记录中间结果,避免重复计算,提高效率。
  • 递归函数中每次选择一个硬币面值,尝试组合达到目标金额,找出最少的硬币数量。
  • 如果某个金额已经计算过,直接返回已存储的结果,避免重复计算,提高效率。

代码

    int recurison(vector<int>& arr, int aim, vector<int>& dp)
    {
        if(aim < 0) return -1;

        if(aim == 0) return 0;
        
        if(dp[aim - 1] != 0)
            return dp[aim - 1];

        int Min = INT_MAX;
        //遍历所有的值
        for(int i = 0; i < arr.size(); ++i)
        {
            //递归运算选择当前的面值
            int res = recurison(arr, aim - arr[i], dp);

            //获取最小值
            if(res >= 0 && res < Min)
            Min = res + 1;
        }
        //更新最小值
        dp[aim - 1] = Min == INT_MAX ? -1 : Min;
        
        return dp[aim - 1]; 
    }
    
    int minMoney(vector<int>& arr, int aim) 
    {
        if(aim < 0) return -1;
        vector<int> dp(aim, 0);

        return recurison(arr, aim, dp);
    }

复杂度

  • 时间复杂度: O ( n ∗ a i m ) O(n * aim) O(naim),一共需要计算aim个状态的答案,每个状态需要枚举n个面值
  • 空间复杂度: O ( a i m ) O(aim) O(aim),递归栈深度及辅助数组的空间
  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米睡不醒.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值