考虑以下问题
给予无限数量的镍(5美分)和便士(1美分)。编写一个代码来计算代表n美分的多种方式。
我的代码
def coins(n):
if (n < 0):
return 0
elif (n == 0):
return 1
else:
if (cnt_lst[n-1] == 0):
cnt_lst[n-1] = coins(n-1) + coins(n-5)
return cnt_lst[n-1]
if __name__ == "__main__":
cnt = int(input())
cnt_lst = [0] * cnt #Memiozation
ret = coins(cnt)
print(ret)
以上方法计算不止一个重复模式(显然我没有明确地检查它们)。
[5,1,1,1,1,1] [1,5,1,1,1,1] [1,1,5,1,1,1]等
维护另一个包含先前看到的模式的列表,随着n
的增长需要大量内存。我们可以用另一种方法来解决这个问题吗?
答案 0 :(得分:2)
虽然你的实现是典型的Coin Change Problem的扭曲,1维度更少,这可能会导致你重复计算很多组合,我认为给定的硬币系统并不复杂。
我认为解决方案只是 floor(N/5) + 1
由于你只能使用5美分的[0..N/5]
,剩下的其他你必须使用1美分
这可以很容易地完成,因为您的硬币系统是规范的,并且可以应用贪婪的算法/思维模式。
如果您坚持使用动态编程来解决问题,则需要添加一个维度,表示正在使用的硬币类型。这种方法适用于任何硬币系统
定义C(x, m):= The # of ways to make number x using first m type of coins
所以现在递推公式变为:
C(x, m) = C(x-coin_type[m], m) + C(x, m-1)
,表示您选择使用第m种硬币,或不使用它
以下是主要观点,即为什么这种重复发生并且你的工作不起作用,状态的迭代次序。
使用此递推公式,我们可以执行类似
的操作For i = 0 to # of coin_type
For j = 0 to n
C(j, i) = C(j-coin_type[i], i) + C(j, i-1)
注意外环强制硬币类型的迭代排序。假设coin_type = {1,3,5}
,循环将首先计算仅使用{1}的方式,然后循环的下一次迭代将计算使用{1,3}的方式,最后一次迭代将使用{1计算方式, 3,5}。
你永远不会计算使用{3,1,5}或{1,5,3}等的方式。硬币类型的顺序是固定的,这意味着你不会重复计算任何东西
答案 1 :(得分:2)
您可以使用二维数组,而不是使用一维列表,其中一维是总数,第二维是可用的最大值硬币。
假设C
是您按升序排列的硬币值列表(在您的示例中为C = [1, 5]
)。然后,A[i][j]
是用硬币i
到0
来表示价值j
的方式的数量。
我们知道,对于任何j
,A[0][j] = 1
,因为只有一种方法可以表示值0
:没有硬币。
现在假设我们想要找到A[8][1]
,用便士和镍币代表8美分的方式。每个表示将使用镍或不会。如果我们不使用镍,那么我们只能使用便士,所以有A[8][0]
方法可以做到这一点。如果我们确实使用了镍,那么我们剩下3
美分,因此有A[3][1]
个方法可以做到这一点。
要计算A[8][0]
我们只有一枚可用的硬币A[8][0] = A[7][0] = ... = A[0][0] = 1
。
要计算A[3][1]
,我们无法使用3 < 5
以来的镍,因此A[3][1] = A[3][0]
。从那里我们有A[3][0] = A[2][0] = ... = 1
,如上所述。
一般来说:
A[i][j] = A[i][j-1] if i < C[j]
else A[i][j-1] + A[i-C[j]][j]
此算法适用于任何一组硬币值。