我正在练习动态编程。我专注于硬币交换问题的以下变体:
让n
成为整数硬币面额的常数集。让S
为A
中通过硬币可获得的正整数金额。考虑两个人B
和n
。我可以在A
和B
之间以多少种方式分割n = 6
,以便每个人获得相同数量的硬币(无视每个人获得的实际金额)?
示例
A
可以分为4种不同的方式:
B
获得{2,2},而人A
获得{1,1}。B
获得{2,1},而人A
获得{2,1}。B
获得{1,1},而人A
获得{2,2}。B
获得{1,1,1},而人def f(n, coins):
if n < 0:
return 0
if n == 0:
return 1
return sum([f(n - coin, coins) for coin in coins])
获得{1,1,1}。请注意,每种方式都是非冗余的,即我们不会将{2,1}和{1,2}视为两种不同的方式。
之前的研究
我研究过非常相似的DP问题,例如硬币交换问题和分区问题。事实上,这个网站中有一些问题涉及几乎相同的问题:
我最感兴趣的是递归关系可以帮助我解决这个问题。定义它将允许我轻松应用表格方法的记忆来设计这个问题的算法。
例如,这个递归:
# => f(6, [1, 2, 6]) # 14
是诱人的,但它不起作用,因为执行时:
S' = {1, 2, 6}
以下是n = 6
和 let cell = collectionView.dequeueReusableCell(withReuseIdentifier:
"cell", for: indexPath) as! UICollectionViewCell
的竞选示例,以帮助我澄清模式(可能存在错误):
答案 0 :(得分:2)
这是你可以尝试的:
C(n, k, S)
使用来自n
的{{1}}个硬币,k
为S
金额的不同表示形式。
然后C(n, k, S) = sum(C(n - s_i, k - 1, S[i:]))
总和来自s_i
的每S
。 S[i:]
表示S
从i
- 元素到结尾的所有元素 - 我们需要这样来防止重复组合。
如果C(0, 0, _) = 1
或C(n, k, _) = 0
或n < 0
和k < 0
,初始条件为n > 0
和k < 1
。
您要计算的数字:
R = sum(C(i, k, S) * C(n - i, k, S))
i = 1..n-1
k = 1..min(i, n-i)/Smin
,Smin
S
- 来自min(i, n-i)/Smin
的最小硬币面额。n = 20
值i = 8
表示分割给定总和时可能的最大硬币数。例如,如果总和8/2 = 4
和>4
(第一人获得$ 8,第二人获得$ 12)且最小硬币面额为$ 2,则最大可能的硬币数为jaotc
。 <* 1}}硬币无法获得8美元。
答案 1 :(得分:1)
这是一个表实现,并对algrid's beautiful answer进行了一些阐述。这会在约2秒内为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]
不会包含6
1}}和前一个子集[2, 6, 12, 24, 48, 60]
至少有一个2
硬币。
import time
def f(n, coins):
t0 = time.time()
min_coins = min(coins)
m = [[[0] * len(coins) for k in xrange(n / min_coins + 1)] for _n in xrange(n + 1)]
# Initialize base case
for i in xrange(len(coins)):
m[0][0][i] = 1
for i in xrange(len(coins)):
for _i in xrange(i + 1):
for _n in xrange(coins[_i], n + 1):
for k in xrange(1, _n / min_coins + 1):
m[_n][k][i] += m[_n - coins[_i]][k - 1][_i]
result = 0
for a in xrange(1, n + 1):
b = n - a
for k in xrange(1, n / min_coins + 1):
result = result + m[a][k][len(coins) - 1] * m[b][k][len(coins) - 1]
total_time = time.time() - t0
return (result, total_time)
print f(500, [1, 2, 6, 12, 24, 48, 60])