以下是我们的问题:
把100元用50元,20元,10元,5元,1元去换,总共有几种换法?
形式化(formalize)
为了简化叙述,把问题记为“coc(100, [50, 20, 10, 5, 1])”。
coc: case of change或者count of change(change在英文中有零钱的意思)
为使问题更具有一般性,参数化具体值,得到:
caseOfChange(amount, cashList)
其中amount为金额,cashList为零钱列表。
递归分析
前面说到递归分两种情形,一是 基本情形(base case) ,二是 递归情形(recursive case) 。
基本情形(base case)
显然,问题难在零钱种类太多,如果只有一种零钱,问题就简单了:
比如coc(100, [50])=1,即有一种换法,也就是换成2张50的。
又如coc(50, [20])=0,即没法换,2张20不够,3张又太多。
显然,结果不是0就是1,就看能否整除。根据以上分析,得出:
caseOfChange(amount, cashList) {
// base case
if (cashList.containsOnlyOneCash()) { // 只有一种零钱时
if (amount.canBeChangedWith(cashList.getTheOnlyOneCash())) { // 能换时,即能整除
return 1;
} else {
return 0;
}
} else {
// recursive case, TODO
}
}
注:暂时请只专注于描述我们的问题,现在还不急着考虑具体的实现。
递归情形(recursive case)
因为递归的模式并不是那么明显,我们还是先回到传统的思路上考虑。
拆分问题
问题难在零钱种类太多,我们首先考虑能否拆解它。
把大问题分成几个小问题,或者说把一个复杂问题分成几个简单问题,这是我们常用的伎俩。
那么怎么分解呢?我们经常能想到的就是一分为二。
如果我们能找到一种方法,比方说,能把5种零钱拆成“1+4”,那么“1”可以在base case中解决了,更进一步,4又可拆成1+3,循环往复,问题就可解决了。