我正在练习动态编程。我专注于硬币交换问题的以下变体:
让S = [1, 2, 6, 12, 24, 48, 60]
成为一组恒定的整数硬币面额。让n
成为通过S
中的硬币可获得的正整数金额。考虑两个人A
和B
。在多少种不同的方式我可以在n
和A
之间拆分B
,以便每个人获得相同数量的硬币(无视每个人获得的实际金额)?
例
n = 6
可以分为4种不同的方式:
A
获得{2,2},人B
获得{1,1}。A
获得{2,1},人B
获得{2,1}。A
获得{1,1},人B
得到{2,2}。A
获得{1,1,1},人B
获得{1,1,1}。请注意,每种方式都是非冗余的,即我们不将{2,1}和{1,2}都视为两种不同的方式。
以前的研究
我研究过非常相似的DP问题,例如硬币交换问题和分区问题。事实上,本网站中存在的问题涉及几乎相同的问题:
我主要感兴趣的是递归关系可以帮助我解决这个问题。定义它将允许我轻松地应用表格方法的记忆来设计针对该问题的算法。
例如,这个递归:
def f(n, coins):
if n < 0:
return 0
if n == 0:
return 1
return sum([f(n - coin, coins) for coin in coins])
很诱人,但它不起作用,因为执行时:
# => f(6, [1, 2, 6]) # 14
以下是S' = {1, 2, 6}
和n = 6
运行的示例,以帮助我澄清模式(可能存在错误):
这是你可以尝试的:
让C(n, k, S)
使用来自n
的一些k
硬币,数量S
的不同表示。
然后C(n, k, S) = sum(C(n - s_i, k - 1, S[i:]))
总和是来自s_i
的每个S
。 S[i:]
是指S
从i
-th元素到结尾的所有元素 - 我们需要这个来防止重复组合。
如果C(0, 0, _) = 1
或C(n, k, _) = 0
或n < 0
和k < 0
,最初的条件是n > 0
和k < 1
。
您要计算的数字:
qazxsw poi for qazxsw poi,qazxsw poi R = sum(C(i, k, S) * C(n - i, k, S))
- i = 1..n-1
最小的硬币面额。
值k = 1..min(i, n-i)/Smin
表示分割给定总和时可能的最大硬币数。例如,如果总和Smin
和S
(第一人获得8美元,第二人获得12美元)并且最小硬币面额是2美元,则最大可能的硬币数量是min(i, n-i)/Smin
。你不能用n = 20
硬币获得8美元。
这是一个表实现和i = 8
的一点点阐述。这会在大约2秒内为8/2 = 4
产生答案。
>4
的简单声明意味着使用algrid's beautiful answer硬币添加所有方法来获得当前总和f(500, [1, 2, 6, 12, 24, 48, 60])
。然后,如果我们将C(n, k, S) = sum(C(n - s_i, k - 1, S[i:]))
分成所有方式,它可以分成两部分,我们可以添加所有这些部分的所有方式可以使用相同的数字n
,硬币。
将我们选择的硬币子集固定到递减列表的美妙意味着任何硬币的任意组合只会被计算一次 - 它将在计算中计算,其中组合中最左边的硬币是我们减少子集中的第一个硬币(假设我们以相同的方式订购它们)。例如,取自k
的任意子集n
将仅计入子集k
的总和,因为下一个子集,[6, 24, 48]
将不包括[1, 2, 6, 12, 24, 48, 60]
,并且先前子集[6, 12, 24, 48, 60]
具有至少一个[12, 24, 48, 60]
硬币。
Python代码(见6
;确认[2, 6, 12, 24, 48, 60]
):
2