我正在尝试使用递归方法解决硬币更改问题。问题是这样的:
您将获得不同面额和总金额的硬币。编写一个函数来计算构成该数量的组合数。
您将获得一笔金额和一系列硬币。
这是我到目前为止所做的:
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.
答案 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