硬币改变递归方法

时间:2017-07-30 16:04:01

标签: java recursion

我正在尝试使用递归方法解决硬币更改问题。问题是这样的:

  

您将获得不同面额和总金额的硬币。编写一个函数来计算构成该数量的组合数。

     

您将获得一笔金额和一系列硬币。

这是我到目前为止所做的:

private static int[] coins = {1,2,5};

public static void main(String[] args) {
    System.out.println(count(13));
}

public static int count(int n)
{
    // If n is 0 then there is 1 solution
    if (n == 0)
        return 1;

    // If n is less than 0 then no solution exists
    if (n < 0)
        return 0;

    return count(n - coins[0]) + count(n - coins[1]) + count(n - coins[2]);
}

当我这样做时,我没有接近正确的组合。我认为问题在于回归,但我无法弄清楚原因。在这里,我从金额中减去硬币并每次将它们加在一起。当它变为0时,它返回1.

4 个答案:

答案 0 :(得分:6)

首先你应该替换:

return count(n - coins[0]) + count(n - coins[1]) + count(n - coins[2]);

循环:

int nCoins = 0;
for(int coin=0; coin<coins.length; coin++)
{
    nCoins += count(n-coins[coin]);
}
return nCoins;

这样做的麻烦在于它会产生硬币的所有排列(= 634)。要获得每个独特的硬币组合,您需要确保以当前硬币开始每个级别的递归。为此,您需要为count方法添加一个参数,以指示硬币数组中要开始的位置。

public static int count(int n, int startCoin)

然后你的循环变为

int nCoins = 0;
for(int coin=startCoin; coin<coins.length; coin++)
{
    nCoins += count(n-coins[coin], coin);
}
return nCoins;

给出正确答案(14)。

答案 1 :(得分:3)

已经发布了这个问题的解决方案,所以我假设您正在询问如何思考它,而不是答案本身。

试试这个:

设V为目标值。

设C [i]为第i个硬币值。

递归解决方案是做出一个减少问题大小的选择,然后在较小的问题上递归使用相同的解决方案。

当问题小到足以轻易解决而不再发生时,我们只需返回该解决方案。这是&#34;基本情况。&#34;

这里的选择是使用具有值C [i]的特定数量N [i]的硬币。

我们需要对所有可能的N [i]值,即N [i] = 0,1,2,... floor(V / C [i])。整数层(V / C [i])只是可能产生正确变化的第i个硬币的最大数量。

一旦我们选择使用了多少枚硬币,我们就不应该改变这个决定。 (这是你的解决方案出错的地方。)最简单的方法是利用硬币的隐式顺序,因为它们的数组索引。我们递归地找到使用硬币i + 1和更大的方法来改变目标值的剩余部分的数量:V - N [i] * coins [i]。

(另一种设计是将硬币保存在一组中,然后通过在重复之前从集合中移除硬币[i]来做出选择。但是让我们继续使用索引,因为生成的代码更简单。)

要生成结果,我们只需将所有递归确定的计数相加。

通过这种思考,我们可以为递归方法选择一个签名:

/** 
 * Return the number of ways to make change for the given value 
 * using coins i >= iMin.
 */
int count(int value, int iMin);

时间考虑基础案例。 &#34;成功&#34;当value正好为零时:我们可以通过一无所获来完全改变一种方式! &#34;故障&#34;当value不为零时发生,并且我们没有硬币值来尝试。这恰好是iMin达到coins数组的长度时。

让我们把这个想法付诸代码:

int count(int value, int iMin) {
  if (value == 0) return 1;  // Success base case.
  if (iMin >= coins.length) return 0; // Failure base case.
  result = 0;
  for (int ni = 0; ni <= value / coins[iMin]; ++ni)
    result += count(value - ni * coins[iMin], iMin + 1);
  return result;
}

要开始递归,只需使用目标值和零iMin

int result = count(target, 0);

请注意,尽管此解决方案是正确的,但它并不是那么有效。让我们讨论另一天。

答案 2 :(得分:0)

根据你的算法,便士然后将镍视为镍和便士的不同解决方案。您应该执行特定订单。 (这在数学中称为排列和组合之间的差异。)

我会考虑添加一个硬币面额列表作为递归函数的第二个参数。然后,在每个步骤(终端步骤除外),您将考虑两种可能性:

A)考虑添加另一枚硬币的可能性,但只是列表前面的一种面额

B)考虑递归调用的可能性,其中你要删除列表中的第一个元素

答案 3 :(得分:0)

我还没有足够的声誉发表评论,目前我正致力于解决您的问题。这是我在您当前的代码中注意到的一个缺陷:您正在跟踪&#34;独特的排列&#34; (我不知道官方的数学名称是什么)而不是&#34;类似的排列,&#34;因为我相信你想。

例如,如果您想找到BrowserAnimationsModule,您将获得以下九(9)种可能的排列方式/到达5的方式:

[2,2,1],[2,1,2],[1,2,2],[5],[2,1,1,1],[1,2,1,1] ,[1,1,2,1],[1,1,1,2]和[1,1,1,1,1]。

虽然我相信您只想为以下排列返回四(4):

[1,1,1,1,1],[2,1,1],[2,2,1],[5]

以下链接我认为会进一步回答您的问题。请在将来提问之前搜索Stack Overflow!

How to count possible combination for coin problem

How to find all combinations of coins when given some dollar value

相关问题