动态规划最优硬币变化

时间:2012-12-06 20:02:55

标签: python algorithm dynamic-programming

我一直在审查一些动态编程问题,而且我很难绕过一些代码来查找最少数量的硬币来进行更改。

假设我们有25,10和1的硬币,我们正在进行30次更改。贪婪将返回25和5(1),而最佳解决方案将返回3(10)。以下是本书中有关此问题的代码:

def dpMakeChange(coinValueList,change,minCoins):
   for cents in range(change+1):
      coinCount = cents
      for j in [c for c in coinValueList if c <= cents]:
            if minCoins[cents-j] + 1 < coinCount:
               coinCount = minCoins[cents-j]+1
      minCoins[cents] = coinCount
   return minCoins[change]

如果有人能帮助我绕过这段代码(第4行是我开始感到困惑的地方),那就太好了。谢谢!

3 个答案:

答案 0 :(得分:5)

在我看来,代码正在解决问题,每一分钱值直到目标分值。给定目标值v和一组硬币C,您知道最佳硬币选择S必须采用union(S', c)形式,其中c来自C的某些硬币和S'v - value(c)的最佳解决方案(请原谅我的注释)。所以问题有optimal substructure。动态编程方法是解决每个可能的子问题。这需要cents * size(C)个步骤,而不是如果你只是试图强行直接解决方案那么会爆炸得更快。

def dpMakeChange(coinValueList,change,minCoins):
   # Solve the problem for each number of cents less than the target
   for cents in range(change+1):

      # At worst, it takes all pennies, so make that the base solution
      coinCount = cents

      # Try all coin values less than the current number of cents
      for j in [c for c in coinValueList if c <= cents]:

            # See if a solution to current number of cents minus the value
            # of the current coin, with one more coin added is the best 
            # solution so far  
            if minCoins[cents-j] + 1 < coinCount:
               coinCount = minCoins[cents-j]+1

      # Memoize the solution for the current number of cents
      minCoins[cents] = coinCount

   # By the time we're here, we've built the solution to the overall problem, 
   # so return it
   return minCoins[change]

答案 1 :(得分:4)

如果你对图论有兴趣,可以考虑一下可能有用的硬币变化问题。

假设您有以下列方式定义的图表:

  • 每个单位的金额(例如,便士)都有一个节点,从0到您感兴趣的价值(例如,39美分或其他。)
  • 在任意两个节点之间有一个弧,它与您允许使用的硬币的值完全相隔(例如,如果您允许使用镍币,则节点在34美分到29美分之间。)

现在您可以将硬币更改问题视为从您感兴趣的值到零的最短路径问题,因为硬币的数量将与您路径中的弧数完全相同。

该算法不使用图论理论术语,但它基本上做同样的事情:外部循环覆盖所有“美分”(或图论框架中的节点),内部循环是范围从当前弧到下一个弧的所有弧(coinValueList中的值)。总之,他们正在寻找从零到您感兴趣的价值的最短路径。 (值降至零,零至值,无关紧要。传统上我们向下搜索零。)

当我意识到许多问题可能被视为图形问题时,我才真正开始理解动态编程。 (但要小心 - 不是所有人都可以。有些是超图,有些甚至可能不是。但它对我帮助很大。)

答案 2 :(得分:3)

我认为第四行令人困惑,因为虽然Python可以在列表推导(transform(x) for x in iterable if condition(x))中选择/过滤,但它在标准for x in iterable:表达式中不能做同样的事情。

所以人们绕过的一种(俗气的)方式是将两者焊接在一起。他们创建了一个列表理解,它实际上没有转换(因此c for c in coinValueList),因此他们可以添加if c <= cents子句。然后将其用作标准for x in iterable:表达式的可迭代。我怀疑这是你的一些混乱所在。

编写该行的另一种方法可能是:

...
for eachCoinValue in filter(lambda x: x <= cents, coinValueList):
...

或者更清楚的是,“意图揭示变量”将是:

...
smallEnoughCoins = filter(lambda each: each <= cents)
for each in smallEnoughCoins:
    ...