Leetcode 322. 零钱兑换——DP问题总结————重复书写硬币问题

力扣322. 零钱兑换(动态规划,Java/C/Python3实现含注释说明,中等) 动态规划是解决这类问题的有效方法,因为它避免了重复计算子问题,从而提高了效率。在记忆化搜索和动态规划之间选择时,如果问题的规模较小或者递归结构清晰,记忆化搜索可能更为直观。然而,对于大规模问题或需要优化的场景,动态规划通常更加高效。无论是记忆化搜索还是动态规划,它们的核心思想都是将原问题分解为子问题,并存储子问题的解以避免重复计算。在实际应用中,根据问题的特点和需求,可以选择合适的方法来解决问题。方法时间复杂度空间复杂度优点缺点记忆化搜索O(m * n)O(n)直观易理解,递归结构清晰。 阅读详情

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

原题地址:零钱兑换
或直转到:https://leetcode-cn.com/problems/coin-change/

由于直接上动态规划中等难度的题我有点晕,这直接借鉴一下知乎大佬的文章,并加入自己的理解。

  • 先上代码
int min(int a, int b) {
    return a < b ? a : b;
}

int coinChange(int* coins, int coinsSize, int amount){
    int *value = malloc(sizeof(int) * (amount + 1));
    for (int i = 0; i <= amount; i++) {
        value[i] = amount + 1;
    }
    value[0] = 0;
    for (int i = 1; i <= amount; i++) {
        // 要遍历数组的每个元素,找到所有情况的最优解
        for (int j = 0; j < coinsSize; j++) {
            if (i >= coins[j]) { // i要大于等于数组元素的值
                // 获取value[i]位置的初始值
                int value_i = value[i]; 
                // 获取i减去数组元素的剩余金额
                int remain = i - coins[j]; 
                // 获取之前计算好的剩余金额的最优解
                int value_remain = value[remain];
                // 剩余金额的最优解加一计算出金额i的除了当前面额的最优解
                int count_min_value = value_remain + 1;
                // 比较计算出来的最优解和i金额的初始值,把较小的值赋值给value[i]
                value[i] = min(count_min_value, value_i); 
            }
        }
    }
    if (value[amount] == amount + 1) {
        return -1;
    }
    return value[amount];
}

动态规划(Dunamic Programming)
首先,动态规划问题的一般形式就是求极值,而动态规划的核心问题是穷举,穷举又不是直接进行穷举,而是利用所求解问题的**“重叠子问题”。暴力穷举(我经常使用而且正确率也不高,需要频繁的debug,因为我的思路不是很清晰,自己用脑子很难正确想到所有情况)的效率在一些情况下会很低。
而且,动态规划问题一定具备
“最优子结构”,也正是因为具备最优子结构,我们才可以通过子问题的最值来求解原问题的最值。
另外,尽管动态规划的核心就是穷举求最值,但是问题可以千变万化,穷举真的很困难。但是,我们可以通过列出
[状态转移方程]**来完成正确的穷举。
上面提到的“重叠子问题”、“最优子结构”、“状态转移方程”就是DP问题的三要素。其中,写出状态转移方程是最困难的。

明确 base case -> 明确「状态」-> 明确「选择」 -> 定义 dp 数组/函数的含义。

大体流程如下:

# 初始化 base case
dp[0][0][...] = base
# 进行状态转移
for 状态1 in 状态1的所有取值:
    for 状态2 in 状态2的所有取值:
        for ...
            dp[状态1][状态2][...] = 求最值(选择1,选择2...)
  • Fibonacci

这是C的入门问题,可只有简单的例子才能让我们把精力充分集中在算法背后的通用思想和技巧上,而不会被那些隐晦的细节问题搞的莫名其妙。

1、暴力递归

int fib(int N){
	if(N == 1 || N == 2)return 1;
	return fib(N-1)+fib(N-2);
}

很直接粗暴的递归,代码简洁的过分,但其运行效率却十分低下,整个运算过程很像二叉树向下分支,答案就是顶点,然而必须求解所有分支子问题才能得到最终答案。然而每个问题又是独立的,因此就会徒增计算的复杂度,时间用的更长,内存占的更多。举个例子,其实fib(N-1)中已经包含了子问题fib(N-2),然而在这个递归方法中,fib(N-2)要计算两次,太悲剧了。更低层的计算可能重复的更多。子问题个数为(O(2^n)),解决一个问题的时间就是1,然而就算如此,子问题个数也太多,复杂度爆炸。
兄弟,这就是动态规划问题的第一个性质:重叠子问题

2、嗲备忘录的递归解法
其实明确了问题,问题就已经解决了一半,尽管另一半可能更难。竟然耗费时间的主要原因是重复计算,那我们可以创造一个【备忘录】,每次出现子问题先寻找备忘录,而不是直接进行返回,在return那里递归。如果发现之前已经解决过这个问题了,直接把答案拿来用,不要再耗时间计算了。
这里利用数组充当【备忘录】,也可以使用hash。

int fib(int N) {
    if (N < 1) return 0;
    // 备忘录全初始化为 0
    vector<int> memo(N + 1, 0);//整型容器memo,空间大小为N+1,全为0
    // 进行带备忘录的递归
    return helper(memo, N);
}

int helper(vector<int>& memo, int n) {//将容器的地址放入,话说容器的使用方法好像指针啊,就像把指针打包了。
    // base case 
    if (n == 1 || n == 2) return 1;//这个是最基础的,也是递归的基本,即第一项和第二项都是1.
    // 已经计算过
    if (memo[n] != 0) return memo[n];//查询备忘录
    memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
    return memo[n];
}

1、fib主函数中分了三步走,第一步判断不符合题意的,即"<0"。第二步建立备忘录(数组)存储递归过程中的点,避免重复计算。第三步进行return,这里直接在return处调用helper函数。
2、helper肯定就是得到我们要的答案的,具体也是三步走。第一步判断是否是第一第二次,都是1,这是定义。第二步看备忘录中指定位置是否是0,不是0直接调用后返回,也就是说在之前的递归中已经计算过了,不需要重复计算。第三步对应的肯定是备忘录中n位置是0的,也就是之前的递归中没有计算过的,因此继续进行递归,每次递归都要先查找备忘录,直到返回memo[n]。
3、这个状态转移方程就很灵性,将当前问题分为两个子问题解之和,子问题可以继续在当前函数中寻找子问题的子问题,并优先检索备忘录,降低了复杂度,且递归方式没变。
实际上,使用备忘录的递归算法,把一颗存在巨量冗余的递归树[剪枝]了,改造成了一个不存在冗余的递归图,减少了递归结点的个数。
本题中用到的方法可以称为“自顶向下”,而动态规划问题都是“自底向上”,从问题规模最小的f(1)和f(2)开始往上推,直到推到我们想要的答案,这就是动态规划的思路。这也是动态规划可以一定程度上脱离递归的关系。

备忘录的制作:

int fib(int N) {
    vector<int> dp(N + 1, 0);//容器的创建
    // base case
    dp[1] = dp[2] = 1;
    for (int i = 3; i <= N; i++)
        dp[i] = dp[i - 1] + dp[i - 2];//一个循环搞定,只要向后递推,所需的子问题解都已经得到了
    return dp[N];
}

下面引出**[状态转移方程]一词。想象一下f(n)就是由f(n-1)与f(n-2)相加转移而来,这就是状态转移,仅此而已。
上面的所有代码中,不管是return f(n-1)+f(n-2)还是dp[i]=dp[i-1]+dp[n-2]以及备忘录对DP table的初始化操作,都是围绕这个方程式的不同表现形式。可见列出
[状态转移方程]**的重要性。状态转移方程也直接代表着暴力解法。
这里知乎博主提到了一个很有意思的问题:

千万不要看不起暴力解,动态规划问题最困难的就是写出这个暴力解,即状态转移方程。
只要写出了暴力解,任何优化方法,也再无奥妙可言。

难道就这样了么?利用了DP table就无法继续优化了么?根据Fabonacci状态转移方程,当前状态只和之前的两个状态有关,也就是说并不需要那么长的DP table来存储所有状态,只要想办法存储之前两个状态就行了。所以可以进一步优化,将空间复杂度降低为O(1):

int fib(int n) {
    if (n == 2 || n == 1) 
        return 1;
    int prev = 1, curr = 1;
    for (int i = 3; i <= n; i++) {
        int sum = prev + curr;
        prev = curr;
        curr = sum;
    }
    return curr;
}

这么想来,DP真的是一个很奇妙的方法,刚开始代码是多么简洁,直接对自身进行递归。可代码的简洁,也带来了极高的复杂度O(2^n)。随着对DP的认识,我们写出了状态转移方程f(n)=f(n-1)+f(n-2),每一次递归我们都将指定结点的解放在一个备忘录中,极大的降低了计算的复杂度,消除了一个结点重复进行计算的情况。为了提高计算效率,在求解次数较高的情况下,我们直接创建一个DP table,当需要求Fibonacci数列的时候直接从里面调用就可以了。若想单次求解最快,而不是利用DP table。我们可以只存储其前两次的解,利用一个循环就可求解,这就是自底向上,而不像自顶向下的递归,不得不对一些子问题进行重复求解。

知乎博主将这一写法成为**[状态压缩]**

如果我们发现每次状态转移只需要 DP table 中的一部分,那么可以尝试用状态压缩来缩小 DP table 的大小,只记录必要的数据,上述例子就相当于把DP table 的大小从 n 缩小到 2。后续的动态规划章节中我们还会看到这样的例子,一般来说是把一个二维的 DP table 压缩成一维,即把空间复杂度从 O(n^2) 压缩到 O(n)。
有人会问,动态规划的另一个重要特性「最优子结构」,怎么没有涉及?下面会涉及。斐波那契数列的例子严格来说不算动态规划,因为没有涉及求最值,以上旨在说明重叠子问题的消除方法,演示得到最优解法逐步求精的过程。下面,看第二个例子,凑零钱问题。

  • 零钱问题~(无限的硬币有谁不想要呢?)

1、暴力递归
首先,这个问题是动态规划问题,因为它具有[最优子结构]。要符合最优子结构,子问题之间必须相互独立。
那么回到凑零钱问题,为什么说它最符合最优子结构呢?比如我们要求amount = 11时的最少硬币数量(原问题),如果你知道凑出 amount = 10的最少硬币数(子问题),你只需要把子问题的答案加一(再选一枚面值为1的硬币)就是原问题的答案。因为硬币的数量是没有限制的,所有例子问题之间没有相互制约,是相互独立的。
如何列出正确的状态转移方程呢?
1、确定base case,这个很简单,显然目标金额为0时算法返回0,因为不需要任何硬币就银镜凑出目标金额了。
2、确定[状态],也就是原问题和子问题中会变化的变量。由于硬币数量无限,硬币的面额也是题目给定的,只有秒爆金额会不断向base case靠近,所以唯一的[状态]就是目标金额 amount。
3、确定[选择],也就是导致[状态]产生变化的行为。目标金额为什么变化呢?因为你在选择硬币,你每选择一枚硬币,就相当于减少了目标金额。所以说所有硬币的面值,就是你的[选择]
4、明确 dp 函数/数组的定义。 我们这里说的是自顶向下的解法,所以会有一个递归的 dp 函数,一般来说函数的参数就是状态转移中会变化的量,也就是说上面提到的[状态];函数的返回值就是题目要求我们计算的量。就本题来说,状态只有一个,即[目标金额],题目要求我们计算凑出目标金额所需的最少硬币数量。我们可以这样定义 dp 函数。

dp(n) 的定义:输入一个目标金额 n ,返回凑出目标金额 n 的最少硬币数量。

伪代码:

# 伪码框架
def coinChange(coins: List[int], amount: int):

    # 定义:要凑出金额 n,至少要 dp(n) 个硬币
    def dp(n):
        # 做选择,选择需要硬币最少的那个结果
        for coin in coins:
            res = min(res, 1 + dp(n - coin))
        return res

    # 题目要求的最终结果是 dp(amount)
    return dp(amount)

2、带备忘录的递归

def coinChange(coins: List[int], amount: int):
    # 备忘录
    memo = dict()
    def dp(n):
        # 查备忘录,避免重复计算
        if n in memo: return memo[n]
        # base case
        if n == 0: return 0
        if n < 0: return -1
        res = float('INF')
        for coin in coins:
            subproblem = dp(n - coin)
            if subproblem == -1: continue
            res = min(res, 1 + subproblem)

        # 记入备忘录
        memo[n] = res if res != float('INF') else -1
        return memo[n]

    return dp(amount)

3、dp 数组的迭代解法
当然,我们也可以自底向上使用DP table来消除重叠子问题,关于**[状态]、[选择]和base case 与之前没有区别,dp数组的定义和刚才dp函数类似,也就是把[状态]**,也就是目标金额作为变量。不过dp函数体现在 函数参数,而ap数组体现在 数组索引。(即寻找转移方程位置)

dp 数组的定义:当目标金额为 i 时,至少需要 dp[i] 枚硬币凑出:

int coinChange(vector<int>& coins, int amount) {
    // 数组大小为 amount + 1,初始值也为 amount + 1
    vector<int> dp(amount + 1, amount + 1);
    // base case
    dp[0] = 0;
    // 外层 for 循环在遍历所有状态的所有取值
    for (int i = 0; i < dp.size(); i++) {
        // 内层 for 循环在求所有选择的最小值
        for (int coin : coins) {
            // 子问题无解,跳过
            if (i - coin < 0) continue;
            dp[i] = min(dp[i], 1 + dp[i - coin]);
        }
    }
    return (dp[amount] == amount + 1) ? -1 : dp[amount];
}
  • 最后总结
    第一个斐波那契数列的问题,解释了如何通过 [备忘录] 或者 [dp table] 的方法来优化递归树,并且明确了这两种方法本质上是一样的,只是自顶向下和自底向上的不同而已。
    第二个凑零钱问题,战术了如何流程化确定 [状态转移方程] ,只要通过状态转移方程写出暴力递归解,剩下的也就是优化递归树,消除重叠子问题而已。

计算机解决问题其实没有任何奇技淫巧,它唯一的解决办法就是穷举


我在编写Fibonacci的时候遇到了一些问题,因为我对C++容器运用的不熟练,在传入备忘录参数的时候没有进行取地址,即:


int myhelp(int n,vector<int>/* & */ index){
    if(n == 1 || n == 2) return 1;
       // myreturn = 1;
    if(index[n]!=-1)     return index[n];
       // myreturn=index[n];
    index[n]=myhelp(n-1,index)+myhelp(n-2,index);
//  myreturn=index[n];
                         return index[n];
}

我把编写的代码简化又简化,结果到最后逻辑上是没有错误的 ,但在每次递归的过程中都新创建了空间,大大增加了时间复杂度。如果直接传入地址,就没有必要在每次传入容器的时候创建新的容器接收,这真的恶心死我了。这里竟然和数组与指针那里这么相似,又有点不一样,数组允许直接传入数组名字,而这个要是直接传入容器名字就会创建新的容器形参来接收,直接传入地址就可以调用实参地址指向的容器!

【hot100-动态规划-322.零钱兑换】 力扣第322题“零钱兑换”要求计算凑成给定总金额所需的最少硬币数。使用动态规划方法,定义 dp[i] 为凑成金额 i 所需的最少硬币数。通过遍历所有硬币面额,更新 dp[i] 的值,状态转移方程为 dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)。最终,若 dp[amount] 不为 Infinity,返回其值,否则返回 -1。时间复杂度为 $O(amount \times n)$,空间复杂度为 $O(amount)$。示例调用展示了代码的正确性。 阅读详情

相关推荐

UE5实战:如何用Mesh Shader优化你的游戏场景渲染(附性能对比)

本文深入探讨了在UE5中利用Mesh Shader优化游戏场景渲染的实战方法。通过对比传统渲染管线与GPU Driven Pipeline,详细阐述了Mesh Shader如何通过任务着色器和网格着色器两阶段并行处理,实现更精细的meshlet级剔除,从而显著降低Draw Call和GPU带宽,提升渲染性能。文章附有具体的性能对比数据与代码示例,为开发者优化高复杂度场景提供了有效路径。

sunny的博客 295

LeetCode-322-零钱兑换

零钱兑换 力扣索引 322. 零钱兑换 518. 零钱兑换 II 零钱兑换 原始问题 给定不同面额的硬币 coins和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。你可以认为每种硬币的数量是无限的。 下面我将以下图举例说明,其中amount=11,coins=[1,2,3],很显然我们在脑海中轻轻的过一遍就能想出由三个面额为3的硬币加上一个面额为2的硬币就组成了最优的答案:4枚、 ![](https://img-b

Neal_caffrey 509

三菱FX5U运动控制(步进)

三菱FX5U脉冲定位控制,原点回归,相对定位,绝对定位,运动控制案例。

weixin_55762091的博客 1万+

【LeetCode】零钱兑换及其升级版(动态规划、回溯法)

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

凝眸伏笔的博客 4243

告别环境配置烦恼:用Conda一键搞定GATK4、samtools、fastp等生信分析全家桶

本文介绍如何利用Conda快速搭建包含GATK4、samtools、fastp等工具的生物信息学分析环境,解决软件安装和版本冲突问题。通过详细的配置步骤和实战案例,帮助研究者十分钟内完成重测序分析全家桶的环境配置,显著提升上游分析效率。

weixin_30753873的博客 334

【微信小程序开发与设计】垃圾智能分类小程序

垃圾分类是将来生活可持续发展的必需项。但是很多人由于缺乏垃圾分类知识,或是因为垃圾分类困难,费时费力,所以没有好好贯彻垃圾分类政策,此时设计一款方便于群众进行智能化垃圾识别分类的微信小程序就显得尤为必要。

Markelle_Fultz的博客 8242

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

/** * 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 * 输入: coins = [1, 2, 5], amount = 11,输出: 3 解释: 11 = 5 + 5 + 1 * 输入: coins = [2], amount = 3,输出: -1 */ public class DP { public static int exchangeRecursive(i.

qq_27215113的博客 5780

leetcode 322. 零钱兑换(动态规划)

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回-1。 示例1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3 输出: -1 说明...

qq_29762941的博客 383

【BigWorld 游戏服务器引擎】Cell 架构设计介绍,及Java实现方案的探索

横向扩展(Scalability):通过动态增减CellApp和BaseApp的数量,可以轻松应对玩家人数的变化。世界可以做得非常大,只需增加更多的 Cell。负载分离(Load Separation):将不同性质的负载分离,避免了单一进程瓶颈。CPU 密集型的实时计算(CellApp)和 I/O 密集型的数据操作(BaseApp)互不影响。高效的视野管理(AOI):每个 Cell 只关心自己区域内和边界上的实体,大大降低了计算复杂度。容错性:某个CellApp。

代码时光 1203

人工智能(AI)简史:推动新时代的科技力量

人工智能(AI,Artificial Intelligence)是计算机科学的一个分支,旨在研究和开发可以模拟、扩展或增强人类智能的系统。它涉及多种技术和方法,包括机器学习、深度学习、自然语言处理(NLP)、计算机视觉、专家系统等。

qq_17153885的博客 1万+

LeetCode 19.凑零钱问题 动态规划

题目描述 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回-1。 示例1: 输入: coins = [1, 2, 5], amount = 11输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2], amount = 3输出: -1 解题思路 动...

Transkai 1125

精益六西格玛Python数字化应用开发-Day18 应用场景:家电企业生产效率优化

本文将重点介绍专门针对精益六西格玛黑带使用场景的 Python 库,这些库功能侧重于数据分析和流程优化,特别适用于 DMAIC 方法论和看板管理。考虑到读者具备中高级Python 使用经验,我们将深入探讨各库的高级功能和实际应用应用场景。 某家电企业生产线自动化程度高,数据量巨大,希望通过精益管理提升生产效率。企业借助 Python 脚本,打通各类数据源(MES、ERP、设备日志),自动归集生产数据,分析各工序的生产节拍、设备停机、效率损失,快速定位瓶颈环节

m0_56592100的博客 905

LeetCode:322. 零钱兑换——动态规划从案例入门

LeetCode:322. 零钱兑换——动态规划从案例入门 🍎道阻且长,行则将至。🍓!!!

Super algorithm 的博客,技术爱好者。 2277

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

leetcode 322. 零钱兑换

零钱兑换解法汇总3.BFS---广度优先遍历2.动态规划3.记忆化递归4.套「完全背包」问题的公式总结 原题链接: leetcode 322. 零钱兑换 3.BFS—广度优先遍历 具体在纸上画一下,就知道这其实是一个在「图」上的最短路径问题,「广度优先遍历」是求解这一类问题的算法。广度优先遍历借助「队列」实现。 注意: 由于是「图」,有回路,所以需要一个 visited 数组,记录哪一些结点已经访问过。 在添加到队列的时候,就得将 visited 数组对应的值设置为 true,否则可能会出现同..

m0_53157173的博客 1027

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

【中等】力扣算法题解析LeetCode322:零钱兑换

LeetCode 322:零钱兑换 - 动态规划解法。给定硬币面额数组coins和目标金额amount,求凑成amount所需的最少硬币数(每种硬币无限使用)。若无法凑出,返回-1。 解法思路: 定义dp数组,dp[i]表示金额i的最少硬币数 初始化dp[0]=0,其他为amount+1(表示不可达) 双重循环遍历金额和硬币: 若当前硬币≤金额,则更新dp[i] = min(dp[i], dp[i-coin]+1) 最终判断dp[amount]是否被更新

ylumwd的博客 900

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

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

文章目录题目解题思路代码(C++)递归优化动态规划总结 题目 题目链接:力扣322. 零钱兑换 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示例 1: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1 示例 2: 输入:coins = [2],

倾心之作 2359

LeetCode 322. 零钱兑换 - 动态规划与BFS优化详解

题目描述:给定不同面额的硬币 coins 和一个总金额 amount,计算凑成总金额所需的最少硬币数。如果无法凑成,返回 -1。每种硬币可以使用任意次。原题链接:https://leetcode.cn/problems/coin-change/难度等级:Medium相关标签:动态规划、完全背包、广度优先搜索(BFS)、贪心(不适用本题)、图论| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 | 适用场景 |

m0_52114506的博客 2578

322. 零钱兑换(leetcode)

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。输入:coins = [1, 2, 5], amount = 11。输入:coins = [2], amount = 3。输入:coins = [1], amount = 0。你可以认为每种硬币的数量是无限的。解释:11 = 5 + 5 + 1。

lulutoy的博客 863

Java 动态规划 - 力扣 零钱兑换与完全平方数 深度解析

深度解析动态规划的Java实现,从爬楼梯故事入手,通过零钱兑换和完全平方数两道经典例题,讲解DP四步解题法、手动推导过程、递推公式推导、常见错误避坑,并提供通用代码模板,助你快速掌握动态规划核心思想,秒杀LeetCode中等DP题。

2301_80567343的博客 1994

LeetCode:322. 零钱兑换(python)

LeetCode:322. 零钱兑换(python) 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 示例 1: 输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1 示例 2: 输入: coins = [2]...

wk的博客 1739

322. 零钱兑换 ——【Leetcode每日一题】

322. 零钱兑换 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。

weixin_43412762的博客 1760

动态规划零钱兑换

1.零钱兑换 Leetcode 322;medium; 数组中给出面额,amount为要兑换到的钱,求兑换出的最少钱币的张数。 我们采用自下而上的方式进行思考。在计算 F(i) 之前,我们先计算出    F(0)-F(i-1) 的答案。 例如:F(11)= min{F(11-1),F(11-2),F(11-5)} + 1 package dynamicprogram; import java....

StackOverflow的博客 2140

力扣解题-322. 零钱兑换

完全背包DP法(第一次解答):O(amount×k)时间+O(amount)空间,逻辑经典,易实现,工程实践首选;BFS解法:O(amount×k)时间+O(amount)空间,实际运行效率更高,找到解后可立即终止;记忆化递归:O(amount×k)时间+O(amount)空间,自顶向下解题,易于理解拆分逻辑;关键技巧核心思想:零钱兑换是典型的完全背包问题,核心是“无限选硬币”的递推/递归逻辑;效率选择:追求代码简洁选DP,追求实际运行速度选BFS,学习阶段可选记忆化递归;

qq_38261626的博客 351

零钱兑换问题

摘要

Jesui的博客 387

leetcode 322:零钱兑换

leetcode 322:零钱兑换 322. 零钱兑换 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示例 1: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1 示例 2: 输入:coins = [2], amount = 3 输出:-1 示例 3:

yingmu__的博客 1823
上一篇: Leetcode 645. 错误的集合
下一篇: Leetcode 2. 两数相加
Neepu_G.job
博客等级 码龄8年 7粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值