硬币问题总结

来源:互联网 发布:pudn程序员 编辑:程序博客网 时间:2024/06/11 05:42

问题描述:假设我们有3种不同面值的硬币{1,2,5},用这些硬币组合够成一个给定的数值n=100,那么有多少种组合方法?

更广泛点:假设有给定p种不同的硬币,面值分别为{v1, v2, ..., vp},和一个金额n,问有多少种组合的方法。

用函数c(m, q)来表示仅仅使用前q种硬币组成金额m的方法数。我们可以用递归式来定义这个函数如下:

c(m, q) = \left\{\begin{matrix}0, & q\leq0 \textup{ or } m<0\\ 1, & m = 0\\ c(m, q-1)+c(m-v_q, q), & 0 < q \leq p\textup{ and } 0 <m \leq n\end{matrix}\right.

之所以加入一个参数q,是为了在缩小问题规模时,除去因为硬币排列顺序的不同而带来的计数重复。比如第三种情况里,仅仅使用前q种硬币组成金额m的方法,可以划为两个分离的部分:仅仅使用前q-1种硬币组成金额m的方法,以及,至少使用了一个第q种硬币来组成金额m的方法。这种划分即没有重复也没有遗漏。而且,"至少使用了一个第q种硬币来组成金额m的方法"的数目等于“仅仅使用前q种硬币来组成金额m-v_q的方法”的数目,因为前者不过是在后者的每一种组合里增加一个第q种硬币。

通过以上的分析,我们可以写出如下的算法来解决这个问题:
Python代码:
def Combines(n,k,v):    if n == 0:        return 1    elif n < 0 or k<= 0:        return 0    else:        return Combines(n,k-1,v) + Combines(n-v[k],k,v)  #return Combines(n,k-1,v) + Combines(n-v[k-1],k,v) 更好点,这时输入v=【1,2,5】
测试代码:n=100,k=3,v=[0,1,2,5]


不使用递归情况下:
n = 100v = [1,2,5]L = [0 for i in range(n+1)]L[0] = 1for i in range(len(v)):for j in range(v[i], len(L)):L[j] += L[j - v[i]]print(L[n])作者:夏之晨链接:https://www.zhihu.com/question/21075235/answer/19566564来源:知乎著作权归作者所有,转载请联系作者获得授权。
转载地址:https://www.zhihu.com/question/21075235


0 0