变硬币交换的动态规划解决方案

时间:2018-05-06 11:48:27

标签: algorithm recursion dynamic-programming coin-change

我正在练习动态编程。我专注于硬币交换问题的以下变体:

n成为整数硬币面额的常数集。让SA中通过硬币可获得的正整数金额。考虑两个人Bn。我可以在AB之间以多少种方式分割n = 6,以便每个人获得相同数量的硬币(无视每个人获得的实际金额)?

示例

A可以分为4种不同的方式:

  1. B获得{2,2},而人A获得{1,1}。
  2. B获得{2,1},而人A获得{2,1}。
  3. B获得{1,1},而人A获得{2,2}。
  4. 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}。
  5. 请注意,每种方式都是非冗余的,即我们不会将{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 的竞选示例,以帮助我澄清模式(可能存在错误):

    Example for S' = {1, 2, 6} and n = 6

2 个答案:

答案 0 :(得分:2)

这是你可以尝试的:

C(n, k, S)使用来自n的{​​{1}}个硬币,kS金额的不同表示形式。

然后C(n, k, S) = sum(C(n - s_i, k - 1, S[i:]))总和来自s_i的每SS[i:]表示Si - 元素到结尾的所有元素 - 我们需要这样来防止重复组合。

如果C(0, 0, _) = 1C(n, k, _) = 0n < 0k < 0,初始条件为n > 0k < 1

您要计算的数字:

R = sum(C(i, k, S) * C(n - i, k, S)) i = 1..n-1 k = 1..min(i, n-i)/SminSmin 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硬币。

Python代码(请参阅here;确认here):

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])