换零钱的算法
问题来自SICP,描述如下
现在若干纸币,想要兑换成硬币。硬币面值有1, 5, 10, 25, 50分的硬币。什么,没有25分的,我说的是美元。。。
问有多少种组合方式
这是介绍线性递归这个概念的时候的一个例子,很有意思。
算法嘛,很朴素啊,其主要思想如下
- 回合制,就是一次递归
- 每一次都可以选择兑换一个面值最大的,或者不兑换
- 剩下的将进入下一轮兑换,直到剩下的钱数为0
scheme的代码就不贴上来了,太简单太抽象。我们还是用C++吧
#include<iomanip>
#include<iostream>
using namespace std;
int coin_value[]={0,1, 5, 10, 25, 50};
int coin_count[]={0,0, 0, 0, 0, 0};
int entry_count;
int* matrix;
int cc(int money, int coin_type,int lr)
{
int val = 0;
int vala = 0;
int valb = 0;
entry_count++;
// cout<<"("<< setw(4) <<money<<","<< setw(4) <<coin_value[coin_type]<<") ==> ";
if (lr==0)
{
int a = sizeof(coin_count);
memset(coin_count, 0, sizeof(coin_count));
}
if (money ==0)
{
val = 1;
}
else if ((money<0) || (coin_type==0))
{
val = 0;
}
// else if (matrix[money*6+coin_type] >=0)
// {
// val = matrix[money*6+coin_type];
// }
else
{
vala = cc(money, coin_type-1,0);
valb = cc(money-coin_value[coin_type], coin_type, 1);
val = vala + valb;
if (vala == 0) coin_count[coin_type] += valb;
else coin_count[coin_type] = valb;
if (money >=0 )matrix[money*6+coin_type] = val;
}
// cout<<"("<< setw(4) <<money<<","<< setw(4) <<coin_value[coin_type]<<", ";
// cout<<setw(4) << val <<", "<<(lr==0? "left" :"right") <<")"<<endl;
return val;
}
int main()
{
int money = 100;
matrix = new int[(money+1)*sizeof(coin_value)/sizeof(int)];
memset(matrix, -1, (money+1)*sizeof(coin_value));
entry_count = 0;
cout <<"method = " <<cc(money, 5, 0);
cout << ", calls= "<<entry_count<<endl;
delete[] matrix;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
上面代码用标准递归,被注释掉的那块else if代码是查找已经计算过的节点的值,用来减少递归调用次数。
金额(美分) | 10 | 100 | 200 | 500 |
---|---|---|---|---|
组合方式 | 4 | 292 | 2435 | 59576 |
查找表方式递归次数 | 31 | 289 | 581 | 1457 |
简单递归方式递归次数 | 41 | 15499 | 229589 | 12822611 |
可以看到用查找表方式,算法的时间复杂度是O(n),简单递归的时间复杂度是O(n^m)。不过查找表方式 空间复杂度是O(n*m),简单递归的空间复杂度是O(n+m)。其中,n是金额,m是硬币种类数。
那么,如果我们想知道每种组合的具体配置,how?