【问题标题】:What are the base cases for Coin Change using Recursion?使用递归进行硬币兑换的基本情况是什么?
【发布时间】:2015-01-23 03:15:24
【问题描述】:

我基本上是在尝试通过递归来解决硬币找零问题,这就是我目前所拥有的 -:

#include<iostream>
#include<conio.h>
using namespace std;

int a[]={1,2,5,10,20,50,100,200},count=0;

//i is the array index we are working at
//a[] contains the list of the denominations
//count keeps track of the number of possibilities

void s(int i,int sum) //the function that i wrote
{
    if (!( i>7 || sum<0 || (i==7 && sum!=0) )){

    if (sum==0) ++count; 

    s(i+1,sum);
    s(i,sum-a[i]);

    }
}


int c(int sum,int  i ){  //the function that I took from the algorithmist
    if (sum == 0)
        return 1;
    if (sum < 0)
        return 0;
    if (i <= 0 && sum > 0 )
        return 1;

    return (c( sum - a[i], i ) + c( sum, i - 1 ));
}
int main()
{
    int a;
    cin>>a;

    s(0,a);
    cout<<c(a,7)<<endl<<count;

    getch();
    return 0;
}

第一个函数 s(i,sum) 由我编写,第二个函数 c(sum,i) 取自这里 - (www.algorithmist.com/index.php/Coin_Change) .

问题在于 count 总是返回比预期更高的值。但是,算法专家的解决方案给出了正确的答案,但我无法理解这个基本情况

if (i <= 0 && sum > 0 ) return 1;

如果索引 (i) 小于或等于 0,并且 sum 仍然不为零,那么函数不应该返回零而不是一吗?

我也知道算法专家的解决方案是正确的,因为在Project Euler,这给了我正确的答案。

【问题讨论】:

  • 你能用英文简单描述一下你的算法吗?这是第一步,从那里您可以实施。我不会使用你复制的算法,也不会使用你实现的算法……但我可以用简单的步骤来描述它,从那里它很容易实现。
  • @DavidRodríguez-dribeas 好吧,我可以根据递归树中分支/节点的打开来描述 - 对于面额数组中的每个成员,我打开一个节点,其中包含该元素sum[i.e s(i,sum-a[i])] 和另一个该元素不包含在 sum 中并且函数移动到数组的下一个元素 [i.e. s(i+1,sum)]。这样每次总和变为零时,我都会增加计数。我做错了什么?
  • 该描述与简单的英语相差甚远,考虑到您正在向您的祖母解释问题和解决方案,您如何提供确切的更改? (通过将问题简化为更小)。例如,要返回价值 X 的零钱,选择最大面额的值,如果 X 大于面额 D,则返回该面额的硬币并解决 X-D 的问题,如果 X 小于 D,则没有更多的硬币可以产生该面额,并尝试通过使用去除 D 的面额子集返回 X。
  • ... 这两个递归步骤可以保证减少问题,或者通过返回较小的值或使用较小的面额集,所以这很好。你什么时候需要停止,当然,如果要返回的值是 0,或者如果你用完了面额(你的面额集不会出现这种情况)。看?简单的英语,没有搜索树或数组或函数......

标签: c++ recursion coin-change


【解决方案1】:

我猜你的问题是“假设我有无限的硬币支持,我可以通过多少种方式更改给定的金额”? 您给出的算法专家解决方案还假设最小面额是1。否则将无法正常工作。 现在你的问题:

if (i <= 0 && sum > 0 ) return 1;

请注意,i&lt;0 的唯一可能性是您使用此值调用它 - 不会使用负值 i 进行递归调用。这种情况(i&lt;0)是一个错误,所以没有正确的结果(也许断言或异常会更好)。 现在,如果i=0,假设在索引0 处有价值硬币1 意味着只有一种方法可以用这种面额交换sum - 给sum 价值硬币1。对吧?

经过片刻思考,我发现了如何消除a[0] == 1 的假设。改变

if (i <= 0 && sum > 0 ) return 1;

进入

if (i <= 0 && sum > 0 ) return sum % a[0] == 0 ? 1 : 0;

【讨论】:

  • 谢谢,这几乎消除了我的困惑。算法学家的函数给出的答案是正确的答案,因为 a[0](在这种特殊情况下)is 等于 1。此外,我弄清楚了为什么我的解决方案不正确 - 在 递归树中的每一层,每当我得到 s(i,sum)==1 时,count 就会增加,这意味着每一种可能性都被计算了很多次!我会相应地修改代码。
【解决方案2】:

我认为算法偏向于面额的选择,并假设只有最小面额的硬币。考虑作为正确性的反例,没有 2 个硬币,只有 1,5,......并且返回的目标是 4:

 (4,1)
    (-1,1)  -> cut, sum<0 a[1]==5
    (4,0)   -> i==0 => 1

要么是那个,要么是你错误地实现了算法(会不会有一个错误?可能是i&lt;0,还是原始数组是从 1 开始的?)

【讨论】:

  • 我很抱歉造成混乱,问题陈述实际上是 - 给定每种硬币的无限供应量,找出求和的方法数。
猜你喜欢
  • 2012-09-19
  • 1970-01-01
  • 1970-01-01
  • 2021-10-01
  • 1970-01-01
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
  • 2014-01-19
相关资源
最近更新 更多