Java 换币递归法
我试图用递归的方法解决硬币兑换问题。问题是这样的: 你会得到不同面额的硬币和总金额。编写一个函数来计算组成该数量的组合数 您将获得一个数量和一组硬币 以下是我到目前为止的情况:Java 换币递归法,java,recursion,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
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]);
}
private static int[]coins={1,2,5};
公共静态void main(字符串[]args){
系统输出打印项次(计数(13));
}
公共静态整数计数(整数n)
{
//如果n为0,则有1个解
如果(n==0)
返回1;
//如果n小于0,则不存在解决方案
if(n<0)
返回0;
返回计数(n-硬币[0])+计数(n-硬币[1])+计数(n-硬币[2]);
}
当我这样做的时候,我没有得到任何接近正确组合的东西。我认为问题在于退货,但我不明白原因。在这里,我从数量中减去硬币,然后每次将它们相加。当它得到0时,返回1。根据您的算法,penny-then-nickel被视为与nickel-then-penny不同的溶液。你应该执行特定的命令。(这在数学上称为排列和组合之间的差异。)
我会考虑添加一个硬币面值表作为递归函数的第二个参数。然后,在每个步骤(除了一个终端步骤),你会考虑两种可能性:
(P> a)考虑添加另一枚硬币的可能性,但只有列表前面的面额之一b)考虑递归调用的可能性,其中你正在截断列表中的第一个元素
,我还没有足够的评论来评论,我目前正在研究解决你的问题。我在您当前的代码中注意到一个缺陷:您正在跟踪“唯一排列”(我不知道正式的数学名称是什么),而不是“类似排列”,我相信您会这样做 例如,如果您想查找计数(5),您将获得以下九(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,2]和[1,1,1,1,1,1] 虽然我相信您只想返回四(4)个以下排列: [1,1,1,1,1],[2,1,1],[2,2,1],[5] 以下是我认为可以进一步回答您问题的链接。请在以后提问之前搜索堆栈溢出这个问题的解决方案已经发布,所以我假设你问的是如何思考它,而不是答案本身 试试这个: 设V为目标值 让C[i]成为第i个硬币的价值 递归解决方案是指做出一个选择,减少问题的规模,然后在较小的问题上递归地使用相同的解决方案 当问题很小,不需要重复就可以轻松解决时,我们只返回该解决方案。这就是“基本情况” 这里的选择是使用一个特定数量的N[i]值为C[i]的硬币 我们需要对N[i]的所有可能值进行分析,即N[i]=0,1,2,…楼层(V/C[i])。整数下限(V/C[i])只是可能产生正确变化的第i个硬币的最大数量 一旦我们决定了使用多少i'th硬币,我们就不应该改变这个决定。(这就是你的解决方案出错的地方。)实现这一点的最简单方法是利用硬币数组索引的隐含顺序。对于目标值的剩余部分:V-N[i]*coins[i],我们递归地找到使用硬币i+1和更大的硬币进行更改的方法的数量 (另一种设计是将硬币保留在一个集合中,并通过在重复出现之前从集合中移除硬币[i]来进行选择。但让我们继续使用索引,因为生成的代码更简单。) 为了产生结果,我们只需将所有递归确定的计数相加 基于这种想法,我们可以为递归方法选择一个签名:
/**
* Return the number of ways to make change for the given value
* using coins i >= iMin.
*/
int count(int value, int iMin);
是时候考虑一下基本情况了。“成功”是指值
完全为零时:我们可以通过完全不做任何事情的方式以完全1的方式进行更改!当值
不为零,并且我们没有硬币值可供尝试时,就会出现“失败”。这正是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;
}
请注意,虽然此解决方案是正确的,但效率不高。让我们把讨论留到另一天。首先,您应该替换:
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;
然后,您的循环变为
int nCoins = 0;
for(int coin=startCoin; coin<coins.length; coin++)
{
nCoins += count(n-coins[coin], coin);
}
return nCoins;
int nCoins=0;
对于(int coin=startCoin;coin除非你改变它,否则你会得到stackoverflow错误…查看调用尾部修剪,在这里你将方法的值存储在你计算的每个值上,所以当你再次调用它时,你只需查看数组而不是重新计算它。你能详细说明一下你的意思吗?@user081608我不明白e你对哪一部分感到困惑,但我已经用更多细节更新了我的答案。如何在此基础上添加备忘录?
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;