我希望提供有关此问题的动态编程解决方案的分步演练。
本文假设读者已经精通递归解决方案。 如果对此有很多疑问,我将通过编辑此帖子进行跟进。 或者我可能还是会回来编辑它。
让我们从看代码开始。 我将使用Python并分解脚本关键部分中发生的情况。 这称为自下而上的方法,因为我们正在逐步提升索引,而不是通过递减索引直到终止来实现递归。
该函数采用S(硬币列表),m(硬币列表的长度)和n(我们要组成的零钱)。 在本文中,我们将使用S = [1,2,3]和n = 4的简单但具有代表性的情况。
在下面的红色框中,我们只是在构造一个列表列表,列表的长度为n + 1。 我们首先将各个列表中的所有值初始化为0。然后,将表中的第一个列表初始化为全1。 为什么会这样呢? 让我们继续了解原因。
首先,让我们开始完全了解并理解下图。 在n = 4和S = [1,2,3]的情况下,
我们将使用的变量如下:S是输入列表,j是跟踪每个S的索引。表中列表的数量初始化为n + 1,以适应n = 0的基本情况。 为什么它们充满“ 1”? 这样,程序将在n = 0时始终返回1,类似于对此问题的递归解决方案。
接下来,让我们看看嵌套的for循环中发生了什么。
我们将遍历表中从索引1开始的每个列表,并且在每次迭代中,我们将遍历S。这样,我们就可以累积计算,并且能够避免重复的计算向前发展。
通过在嵌套的for循环中实现它并增加索引,我们将其称为自底向上方法。
现在到了棘手的部分(至少对我来说是这样)。 让我们关注嵌套的for循环内的两个语句。 从表n = 1开始,对表中每个列表中的每个元素执行这两个语句。
让我们看一下两行代码:
与递归解决方案类似,对于内循环的每次迭代,我们都希望获得强制包含S [j]并将其存储在x中的解,还希望获得强制排除S [j]并将其存储在y中的解。 这样,我们将能够引用较早的解决方案,以避免重复计算。
为什么要这样在每次计算中包含和排除硬币S [j]?
解释很简单。 对于每种硬币,只能有两种可能性。 考虑使用n值包含或排除它。 并且我们知道,如果硬币(S [j])大于n,则x将返回0,因为将其考虑在内是不可能的。
现在,让我们从两个问题开始:
1. i_S [j]的意义是什么?
S [j]之后参考的先前i的值取自i。 强制包含S [j]。
2.为什么引用[j]?
我们想引用S中相同的位置,即S [j],只是在不同的n处。
3.为什么将它用作表数组的索引?
引用n数组的早期计算。
这两个语句是什么意思:
1. 第一句话
通过使S [j]为强制性,我们从i中减去S [j]并参考过去计算的n,其中n = i_S [j]。
2. 第二条陈述
通过引用先前计算的S [j] = S [j] _1找出所有不包含S [j]的解决方案。让我们深入研究。
让我们深入探讨第二个陈述:
1. [i] [j-1]的意义是什么?
在S的前一个元素中引用相同的n。
2. 为什么当j <1时总是返回0?
当j = 0时,我们不考虑任何S [j]。
3.在这种情况下,列表S是否必须排序?
不,因为我们累积地遍历m朝着j = m的方向努力,并且只对最终情况感兴趣。
4.使用S [0]和S [2]但不使用s [1]的情况如何?
我们不需要进行计算,因为最终我们想知道在迭代硬币数组中的所有硬币之后,对于i == n存在多少个解决方案。
现在,让我们开始运行带有测试值的嵌套for循环。
以上步骤的图示。
我希望这篇文章能够对尝试了解此问题的人有所帮助。 请原谅我没有真正花时间在美学上,希望内容能弥补这一点。
另请参阅Geeksforgeeks网站上的Dynamic Programming Solution,并阅读以下文章:
给定一个值N,如果我们想以N美分作为零钱,那么我们就有S = {S1,S2,..,Sm}中的每一个的无限供应… www.geeksforgeeks.org
From: https://hackernoon.com/the-coin-change-problem-explained-ddd035a8f22f