钱币问题
如果现在有10元钱,有3种零钱,分别是[5,2,1]元,请说出找零钱有多少种方式。
先用手算,我们会分别求出当第一位为5,2,1时的情况。
计算过程如下:
- 以5为开头
- 5 5
- 5 2 2 1
- 5 2 1 1 1
- 5 1 1 1 1 1
- 以2为开头
- 2 2 2 2 2
- 2 2 2 2 1 1
- 2 2 2 1 1 1 1
- 2 2 1 1 1 1 1 1
- 2 1 1 1 1 1 1 1 1
- 以1为开头
- 1 1 1 1 1 1 1 1 1 1
分析:
- 在计算中,我们计算以2为开头时,并不会把5列入,因为我们知道5 2 2 1和2 5 2 1是一样的。
- 计算用了3种,和零钱的种类相同
- 在这期间,结束的标志为当所有零钱加起来等于需要的钱数10,我们就会认为结束了
- 在此期间,我们会自动忽略加起来会超过数值的选项,比如5 2 5
所以我们的思路就是:
-
将给定的钱数看做一个排序好的列表(即使他是[2, 5, 1])
-
对于列表的第一种钱,我们可以选择 拿或者不拿
假定钱数是money = m,可选的种类为kind = k,零钱列表为L
- 不拿,那我们可以拿其他几种,我们可以拿的种类变为 k - 1
- 拿第一种钱,则剩余的钱数为 m - L[0]。剩余的钱可以再从目前所有种类的零钱中再执行步骤2
-
我们对每个都执行步骤2
-
结果判断:
- m = 0,则刚好选择完,钱可以被兑换成零钱,此方案可行。
- k = 0,可选的种类已经没有了。
- m < 0,此方案行不通,例如2 5 5的情况下,m = -2。
Java代码如下:
public class DealMoney {
private static int[] MONEY_LIST = { 2, 5, 1 };
public static int calculate(int money) {
return mainBody(money, MONEY_LIST.length);
}
private static int mainBody(int money, int kind) {
if (money == 0) {
return 1;
}
if (kind <= 0 || money < 0) {
return 0;
}
return not_select(money, kind) + select(money, kind);
}
private static int not_select(int money, int kind) {
kind--;
return mainBody(money, kind);
}
private static int select(int money, int kind) {
int index = MONEY_LIST.length - kind;
return mainBody(money - MONEY_LIST[index], kind);
}
public static void main(String[] args) {
System.out.println(calculate(10));
}
}