当前位置: 首页 > 知识库问答 >
问题:

换币递归法

崔宜修
2023-03-14

我试图用递归的方法解决硬币兑换问题。问题是这样的:

你会得到不同面额的硬币和总金额。编写一个函数来计算组成该数量的组合数。

您将获得一个数量和一组硬币。

这是我到目前为止所拥有的:

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。

共有3个答案

余铭晨
2023-03-14

根据您的算法,penny-then-nickel被视为与nickel-then-penny不同的溶液。你应该执行特定的命令。(这在数学上称为排列和组合之间的差异。)

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

(a)考虑添加另一枚硬币的可能性,但只有列表前面的面额中的一个。

B)考虑递归调用的可能性,其中您正在截断列表中的第一个元素。

沈树
2023-03-14

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

试试这个:

设V为目标值。

让C[i]成为第i个硬币的价值。

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

当问题很小,不需要重复就可以轻松解决时,我们只返回该解决方案。这就是“基本情况”

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

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

一旦我们决定了使用多少i'th硬币,我们就不应该改变这个决定。(这就是您的解决方案出错的地方。)实现这一点的最简单方法是利用货币数组html" target="_blank">索引的隐含顺序。对于目标值的剩余部分: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;
}

要开始递归,只需使用目标值和0的iMin:

int result = count(target, 0);

请注意,虽然此解决方案是正确的,但效率不高。让我们改天再讨论吧。

尉迟招
2023-03-14

首先,你应该替换:

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到100之间的任何值可以得到的更改可能性的数量。我不确定项目中的2个方法做了什么(代码中的粗体部分),所以有人能给我解释一下吗?我对Java还很陌生。 我包含了上下文的整个代码,但不确定是否有必要。

  • 给定一个值N,如果我们想改变为N美分,并且我们有无限量的S={S1,S2,...,Sm}值的硬币,我们有多少种方法可以改变?硬币的顺序并不重要。不过还有一个额外的限制:你只能用正好是K的硬币换零钱。 例如,对于N=4,k=2和S={1,2,3},有两个解:{2,2},{1,3}。所以输出应该是2。 解决方案: 以上是递归解决方案。但是,我需要有关动态规划解决方案的帮助: 让表示与元素和硬币的总和。

  • 我有一个低效的递归硬币变化函数,它计算出给定数量的硬币组合数量。如果可能的话,我想把它转换成一个更有效的迭代函数。 一个问题是,我正在使用回溯来尝试一个叫做面额的数组中的不同硬币。我也在使用记忆法,但当数量很大时,它不会加快速度。 这是我的密码: 有什么想法可以做到这一点吗?我知道有解决硬币更换问题的DP解决方案,但我的解决方案并不容易。我可以有半个便士。 *更新:我将函数改为迭代函数,并将其放大

  • 我试图用递归来解决硬币兑换问题,遇到了下面的代码。 问题:给定一些面额的无限硬币,计算它们形成给定数量的方式。 输入: 代码: 我理解代码本身,也理解基本情况,但是我不理解它是如何封装递归/解决方案的。 例如,如果我正在求解,我可以说,因此我可以清楚地“看到”递归。在换硬币的例子中,我无法推断出类似的关系。有人能帮我吗?

  • 我有一个关于如何将“递归”转换为“尾部递归”的问题。 这不是家庭作业,只是在我试图润色算法书籍中的递归定理时出现的一个问题。 我熟悉使用递归的两个典型示例(阶乘和斐波那契序列),也知道如何以递归方式和尾部递归方式实现它们。 我的代码如下(我使用Perl只是为了使其简单,但可以轻松地转换为C/Java/C)。 运行代码时,输出如下: 递归函数在返回之前使用不同的参数调用自己两次。我尝试了几种方法将其

  • 我想知道是否有一些通用方法可以用foo(…)转换“正常”递归foo(…) 作为尾部递归的最后一个调用。 例如(scala): 函数语言将递归函数转换为等价尾部调用的一般解决方案: 一种简单的方法是将非尾部递归函数包装在蹦床单子中。 所以pascal函数不再是递归函数。然而,蹦床单子是需要完成的计算的嵌套结构。最后,是一个尾递归函数,它遍历树状结构,解释它,最后在基本情况下返回值。 Rúnar Bj