【每日一题】力扣322. 零钱兑换


题目

题目链接:力扣322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

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

示例 1:

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

示例 2:

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

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104

解题思路

看到这题应该可以立马想到是一道 dp 题,如果没怎么接触过 dp ,估计一时半会做不出来。那就先从会的思路开始想。

👉暴力递归(会超时)

没思路的最好办法就是暴力,先暴力出来再想优化。

这题我的第一思路是,先把硬币排序,然后再从大到小开始遍历,对总金额 amount 取余再整除,得到硬币数。但发现样例是:coins = [2, 3], amount = 4 的时候就出错了。所以必须遍历所有情况。

如何遍历所有情况?

递归,把硬币从大到小,取 [0, amount / coin[i]] 范围的个数,计算出最少的硬币个数。把范围内的硬币个数全都遍历一遍即可。递归的深度是硬币数组的长度(如果出现可以直接被总金额整除的硬币,那么可以直接返回,因为遍历是从硬币面值大开始的),递归的宽度是总金额能被当前硬币面额整除的最大硬币数。

递归的深度:从递归开始到第一次 return 的长度

递归的宽度:递归函数里的循环的长度

具体做法:

  • 先把硬币数组排序,再进入递归

  • 递归函数的返回条件:

    • 递归是从硬币面值大的到小的,所以就是遍历硬币数组,所以数组下标不能小于 0
    • 如果递归到当前层次时,硬币的总个数大于之前已经存储的个数,那么这里的递归一定不是最少的硬币个数,可以直接返回
    • 如果发现当前硬币面额可以整除当前总金额,判断和已存储的个数的大小,取小的那个。然后返回
  • 递归函数的参数:

    • 硬币数组
    • 总金额
    • 硬币总个数
    • 硬币数组的下标
  • 递归函数的实现功能

    循环遍历当前面额的硬币可以取的个数([0, amount/coins[i]]

👉优化

上面的方法超时了,原因在于有太多的重复的计算,比如:coins = [1, 2, 3], amount = 7 ,当遍历 3 时,后面会遍历 12 。遍历 2 时,会遍历 1 。这样就出现了重复的情况,可以当 amount 减少一次,就把其对应的所需硬币数存储起来,下次遇到时可以直接使用。

这需要把上面的方法稍微改一下。递归的深度是总金额的大小,递归的宽度是硬币数组的长度。

  • 递归函数的参数

    • 硬币数组
    • 总金额
  • 递归函数的返回条件

    • 当总金额小于 0 时,返回 -1
    • 当总金额等于 0 时,返回 0
    • 当存在当前情况存储的硬币数时,返回存储的硬币数
  • 递归函数的实现功能

    循环遍历硬币数组,将总金额减去硬币面值,放入递归中,取所有的返回值中最小的那个。然后存入数组中

这种方法解决了重复计算所以就没有超时,但不是最优的。

👉动态规划

动态规划感觉就是对上面优化的优化吧。

先给出转移方程吧:F[i] = min(F[i - coins[j]]) + 1,其中 j 的取值范围是 coins 数组的全范围。

因为动态规划都是先从下标小的到下标大的,下标大的需要使用下标小的数组值。所以需要先从开始计算,直到最终。

F[0] = 0F[i] 的取值与 coins 数组和 amount 值有关,所以直接给出核心代码吧:dp[i] = min(dp[i], dp[i - coins[j]] + 1);

知道了这行代码这题就结束了

代码(C++)

递归

class Solution {
   
public:
    int f = INT_MAX;
    void dfs(vector<int
  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

聆听逝去的流

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

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

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

打赏作者

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

抵扣说明:

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

余额充值