当前位置: 首页 > 知识库问答 >
问题:

分为两组的硬币零钱

周和歌
2023-03-14

我试图找出如何解决一个问题,这似乎是一个常见算法问题的棘手变化,但需要额外的逻辑来处理特定的需求。

给定一个硬币列表和一个数量,我需要计算使用无限量可用硬币提取给定数量的可能方法的总数(这是一个典型的改变决策问题)https://en.wikipedia.org/wiki/Change-making_problem 使用动态规划(dynamic programming)轻松解决,同时满足一些附加要求:

  • 提取的硬币可以分成两组大小相等(但不一定相等)
  • 集合中元素的顺序并不重要,但是集合的顺序很重要。

例子

金额为6欧元和硬币[1,2]:解决方案为4

[(1,1), (2,2)]
[(1,1,1), (1,1,1)]
[(2,2), (1,1)]
[(1,2), (1,2)]

金额为8欧元和硬币[1,2,6]:解决方案为7

[(1,1,2), (1,1,2)]
[(1,2,2), (1,1,1)]
[(1,1,1,1), (1,1,1,1)]
[(2), (6)]
[(1,1,1), (1,2,2)]
[(2,2), (2,2)]
[(6), (2)]

现在我尝试了不同的方法,但我发现的唯一方法是收集所有可能的解决方案(使用动态规划),然后过滤不可拆分的解决方案(使用奇数个硬币)和副本。我很确定有一种组合方法可以计算出复制的总数,但我不知道如何计算。

共有2个答案

龚远
2023-03-14

这里有一个表格实现,并对algrid的漂亮答案稍加阐述。这将在大约2秒内产生f(500,[1, 2, 6, 12, 24, 48, 60])的答案。

C(n,k,S)=sum(C(n-S_i,k-1,S[i:])的简单声明意味着使用k硬币添加所有获得当前总和的方法。然后,如果我们将n分成所有可以分成两部分的方式,我们只需添加所有这些部分可以由相同数量的k硬币制成的方式。

将我们选择的硬币子集固定到递减列表的美妙之处在于,任何任意的硬币组合都只会被计算一次——它将被计算在组合中最左边的硬币是我们递减子集中的第一枚硬币的情况下(假设我们以同样的方式订购)。例如,任意子集[6,24,48],取自[1, 2, 6, 12, 24, 48, 60],将只计算在子集[6, 12, 24, 48, 60]的求和中,因为下一个子集,[12, 24, 48, 60]将不包括6并且前一个子集[2, 6, 12, 24, 48, 60]至少有一个2硬币。

Python代码(在这里看到它;在这里确认):

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])
宇文鸿振
2023-03-14

(以下方法首先枚举分区。我的另一个答案是以自下而上的方式生成分配。)如果您希望根据硬币计数计数硬币交换的分割,并排除对每一方的多余硬币分配(例如,将1 2 1分割为相等基数的两部分只是(1,1)|(2,2)(2,2)|(1,1)(1,2)|(1,2),每个部分中的元素顺序并不重要),我们可以依赖于不考虑顺序的分区枚举。

然而,我们需要知道每个分区中的多组元素(或相似元素的集合),以便计算将它们一分为二的可能性。例如,要计算分割方式,我们首先要计算每枚硬币的数量:

Python代码:

def partitions_with_even_number_of_parts_as_multiset(n, coins):
  results = []

  def C(m, n, s, p):
    if n < 0 or m <= 0:
      return

    if n == 0:
      if not p:
        results.append(s)
      return

    C(m - 1, n, s, p)

    _s = s[:]
    _s[m - 1] += 1

    C(m, n - coins[m - 1], _s, not p)

  C(len(coins), n, [0] * len(coins), False)

  return results

输出:

=> partitions_with_even_number_of_parts_as_multiset(6, [1,2,6])
=> [[6, 0, 0], [2, 2, 0]]
                ^ ^ ^ ^ this one represents two 1's and two 2's

现在,由于我们正在计算选择其中一半的方法,我们需要在多项式乘法中找到x^2的系数

(x^2 + x + 1) * (x^2 + x + 1) = ... 3x^2 ...

它表示从多集计数[2,2]中选择两个的三种方法:

2,0 => 1,1
0,2 => 2,2
1,1 => 1,2

在Python中,我们可以使用numpy.polymul来乘多项式系数。然后我们在结果中查找适当的系数。

例如:

import numpy    

def count_split_partitions_by_multiset_count(multiset):
  coefficients = (multiset[0] + 1) * [1]

  for i in xrange(1, len(multiset)):
    coefficients = numpy.polymul(coefficients, (multiset[i] + 1) * [1])

  return coefficients[ sum(multiset) / 2 ]

输出:

=> count_split_partitions_by_multiset_count([2,2,0])
=> 3

(在这里发布了类似的答案。)

 类似资料:
  • 我正在做一个java分配,在那里你输入一个对象的价格和一个理论客户交给你的物品的金额。然后程序返回你欠他们的钱,并将其分解为你应该给他们的美元、二十五美分、一角美分、五美分和便士。

  • 问题内容: 为此,我想将硬币兑换功能转换为记忆功能 ,因此我决定使用字典,以便字典中的键将是硬币,而值将是包含所有可更改“钥匙”的硬币的列表。硬币。 我所做的是: 我想得到一些建议,或者也许有另一种方法可以做到这一点。 谢谢。 编辑 备注版本: 问题答案: 当您可以只使用通用的预先编写的装饰器时,不必编写专门的记忆装饰器。例如,直接来自 PythonDecoratorLibrary 的以下 代码

  • 我试图用记忆和递归来解决硬币兑换的问题。但是我的代码中有一些小故障,它给了我错误的输出。 硬币面额1,2 我要一笔4英镑的总数。 可能的方法是 (1,1,1,1) (1,2,1) (2,2) 我期望输出3,但它返回5

  • 我有这样的数据集: 长度:233333450560650780限制:5400 现在我的问题是,从设定的最高长度到最低长度中选择项目,以弥补限制或尽可能接近。 我知道背包和最小硬币兑换都能解决我的问题。我想知道哪个更好。 请注意,硬币变化是使用贪婪算法,背包使用动态编程

  • 下面是最小硬币变化问题的蛮力解决方案。它需要一个int更改,也就是需要进行的更改,以及一系列硬币面值。它返回进行更改所需的最低硬币。 我如何修改它以同时返回一组硬币? 例如,如果要求给出值为[1,2,5]的10美分的零钱,它应该返回2个硬币分钟和一个数组[0,0,2]的2个硬币。

  • 我正试图解决一个经典的动态规划硬币兑换问题。这是一个家庭作业问题,我不是在寻找完整的解决方案,只是想找几个指针看看我做错了什么。 输入: 金额我们想用硬币支付一些价值 集合表示面值(1c、5c、10c、25c、100c、200c)的可用硬币数量 输出: 需要换手支付的最低硬币数。 假设: 收银机操作员有无限的硬币供应,知道如何给最佳的变化。 到目前为止,我试图做的是: 我试图建立一个数组C,使得每