订单何时重要,如何解决硬币更换任务?

时间:2014-09-10 19:01:45

标签: java algorithm

正如我在here中找到的那样,

硬币变化是使用给定的一组面额d_1 .... d_m找到对特定数量的分数n进行更改的方法的问题。这是整数分区的一般情况,可以通过动态编程来解决。

问题通常被问为:如果我们想要改变N美分,并且每个S = {S_1,S_2,.....,S_m}价值硬币都有无限供应,那么有多少种方法可以我们做出改变? (为简单起见,顺序并不重要。)

我试过这个,这很好用。那么当不同硬币的顺序确实重要时,我如何修改它以找到所有可能的硬币组合

即。 :之前

例如,对于N = 4,S = {1,2,3},有四个解:{1,1,1,1},{1,1,2},{2,2}, {1,3}。

现在:

对于N = 4,S = {1,2,3},有7个解:{1,1,1,1},{1,1,2},{1,2,1},{ 2,1,1},{2,2},{1,3},{3,1}

这里{1,1,1,1}即使可以选择不同顺序的四个'1',也必须将其视为最终组合。而不是考虑四个硬币是不同的。所以实际上不同硬币的顺序必须不同才能将其统计为一个单独的组合。

ex:{1,1,3}不会假设{1_a,1_b,3_a}是组合而{1_b,1_a,3_a}是另一种具有不同排序的组合。

3 个答案:

答案 0 :(得分:7)

计算解决方案的数量远比枚举解决方案要少得多。

让我们以S = {1,2,3}为例,并将f(n)称为金额n的解的数量。

然后我们有:

如果n <1,则

f(n)= 0。 0

f(0)= 1

如果n> 1,则

f(n)= f(n-1)+ f(n-2)+ f(n-3)。 0(其中数字1,2,3是S的元素)

编写执行这些计算的程序并不困难。你可以从较低的数字开始,然后继续努力:

f(0) = 1
f(1) = 1
f(2) = 2
f(3) = 4
f(4) = 7
f(5) = 13
...

对于这个特定的S,结果是每个数字只是前三个数字的总和。

如何达到这个公式?我再次以具体集S = {1,2,3}为例,一般情况同样容易。要计算n的解决方案的数量,请查看第一个元素。它可以是1,2或3.如果是1,则有f(n-1)种方法来排列其余元素。如果它是2,则剩余元素有f(n-2)个方法,最后如果是3,则剩余元素有f(n-3)个方法。因此总数必须是三者的总和。

答案 1 :(得分:2)

如果您指的是&#34;动态编程&#34;参考维基百科页面中的算法,我认为你可以改变

table[ i, j ] = table[ i - S_j, j ] + table[ i, j - 1 ]

table[ i, j ] = table[ i - S_j, m ] + table[ i, j - 1 ]

但我还不是100%肯定。关键是在原始问题中,当您检查硬币S j 时,您希望在金额为i - S j 时添加可能的解决方案的数量但是只有通过S j 的硬币,所以你不能获得前一个序列的排列。通过将其更改为table[i - S_j, m],您可以计算排列数。

编辑:进一步研究,我认为这是正确的,但相当于亨利的答案,这更简单。这个版本的问题(计算所有排列)并不需要将值存储在二维数组中,就像原始数据一样。

答案 2 :(得分:0)

当前的递归实现非常简单。它只传递剩余价值和可能面额的指数。如果你在n == 0时传递了一系列面额,你可以添加另一个检查,看看你是否已经有了这个组合。如果你确实返回0,否则返回1.

这是一个动态解决方案,需要比其他解决方案更多的内存,但它会为您提供答案。

func count(n, m, p[])
//regular if checks with additional nested if in (n == 0)
return count( n, m - 1, p[] ) + count( n - S[m], 0, p[] + S[m] )

在这个伪代码中p [] + S [m]只是意味着将S [m]添加到p []

中的下一个可用位置

编辑:

忘记添加,你需要在潜水时重置m

// n is remaining value
// m is index of S array
// p[] is array of coins used
// solutions[] is an array of p[] arrays
func count( n, m, p[]) {
  if n == 0
    if (p[] not in solutions[]){
      solutions[].add(p[])
      return 1
    }else{
      return 0
    }
  if n < 0
    return 0
  if m <= 0 and n >= 1
    return 0
  return count(n, m - 1, p[]) + count( n - S[m], 0, p[].add(S[m]))
}

每次在可能的集合中添加硬币时,从空数组p []开始,将该值附加到数组。当你找到解决方案时,你会发现它是独一无二的。如果是这样,你将它添加到计数,否则你忽略它。 因为每次遍历所有可能的排列时都会重置m。