美元的硬币面值为1美分、5美分、10美分和25美分。找零钱数为63美分,最少的方案时2个25,1个10,3个1一用6枚硬币。
可以使用贪婪法来找硬币,但是事实并非总是如此。
例如,如果硬币面值多了一个21的,很明显最少的方案是3个21美分的,而不是6个硬币,但,贪婪法无法解决此类问题。即贪婪法只能保证局部最优,不能保证全局最优。
考虑第一种方法,使用递归,计算所有可能组合的最小数目。
首先,最大的方案就是1来填补,但是这里利用的是将63划分为不同的两份,从(1,62)、(2,61)......(31,32),然后以此递归每个分量。这样的计算量堪比fib数列,效率是非常低的。
public static int getMinMoneyR(int[] cons,int money){ int minCoin = money; for(int i=0;i<cons.length;i++){ if(cons[i]==money) return 1; } for(int i=1;i<=money/2;i++){ int thiscoin=getMinMoneyR(cons,i)+getMinMoneyR(cons,money-i); if(thiscoin<minCoin)minCoin=thiscoin; } return minCoin; }
考虑第二种方法,使用动态规划。动态规划的实质是将结果进行保存,每次计算的时候查表,降低计算的复杂度。
public static int getMinMoney(int[] cons,int money){ int mincoin=0; int[] consused = new int[money+1]; //建表查表的过程,针对从1到money的所有可能的最小方案 for(int i=1;i<=money;i++){ mincoin=i;//设置成最大,假设全是1 for(int j=0;j<cons.length;j++){ if(cons[j]<=i){//当硬币面值小于等于i的时候,计算最小硬币数量 //举例子,consused[0]=0,consused[1]=min{1,0+1}=1,consused[2]=min{min{2,1+1},0+1}=1 mincoin=Math.min(mincoin,consused[i-cons[j]]+1); } } consused[i]=mincoin; } return consused[money]; }时间复杂度是O(kn),其中k是硬币的个数,n是要找的零钱数。