Skip to content

Latest commit

 

History

History
75 lines (50 loc) · 2.91 KB

2015-05-21-count-change-recurisve.md

File metadata and controls

75 lines (50 loc) · 2.91 KB

内容

1.2.2 换零钱的递归例子

笔记

  • 换零钱作为树型递归的第二个例子,它比fib数难的一点在于需要判断什么时候返回有效值(也就是1),如何从现实问题抽象出递归模型。

重温下题目:

给了半美元,四分之一美元,10美分,5美分,1美分的硬币,将1美元换成零钱,一共有多少种换法?

将这个题目一般化,是这么一个问题:

将总数为a的现金换成n种硬币,问有多少种不同换法?

这个问题,可以规约为下面两个子问题:

  1. 将现金a换成除第一种硬币以外的其他硬币的不同方式,加上
  2. 将现金a-d换成所有种类硬币的不同方式。其中d为第一种硬币的面值。

子问题1减少了硬币种类,子问题2减少了现金数。子问题比原问题范围要小,终止条件如下:

  1. a=0时,说明已经成功将现金换完,算是一种有效换法
  2. a<0时,说明当前的现金比要兑换硬币面值小,不是有效换法
  3. n=0时,说明当前的现金无法兑换硬币了,不是有效换法。

上面三种情况,第三种不是很明显,下面说一个例子:

假如现在我们不能使用1美分的硬币,而现金还剩3美分,那么其他硬币也就无法兑换了。

(define (exchange amount)
  (cc amount 5))

(define (cc amount kind)
  (cond
    ((= amount 0) 1)
    ((or (< amount 0) (= 0 kind)) 0)
    (else
      (+ (cc amount (- kind 1))
         (cc (- amount (kinds-of-coins kind)) kind)))))

(define (kinds-of-coins kind)
  (cond
    ((= kind 5) 50)
    ((= kind 4) 25)
    ((= kind 3) 10)
    ((= kind 2) 5)
    ((= kind 1) 1)))

(exchange 100)
;Value: 292

换硬币问题我们用递归的方式相对来说解决还不是很难,但是如何用迭代方式解决呢?

首先需要明确的是,所有能用递归方式实现的过程应该都能用迭代方式实现,因为所有的过程在计算机中执行的都是按照图灵机方式(前进、跳转、判断)执行的,我们能不能想出来那又是另一会事了。

迭代方式我现在还没想出来,这里延伸下,这个问题和0-1背包问题很类似,可以类比考虑。

在网上找到了,一个比较合理的迭代解法

思考

如果改变kinds-of-coins中硬币的顺序,程序还正确吗?时空复杂度变了吗?

找零钱问题的复杂度分析可参考习题1.14:(n为现金数)

  • 时间复杂度O(n)
  • 空间复杂度2n

改变kinds-of-coins中硬币的顺序,并不会改变找零钱问题算法树形递归的高度,所以时空复杂度不变。 程序当然也是正确的了,还是符合上面的条件:

  1. 将现金a换成除第一种硬币以外的其他硬币的不同方式,加上
  2. 将现金a-d换成所有种类硬币的不同方式。其中d为第一种硬币的面值。