找零问题:
对于一种货币,有面值c1,c2,....,cN的硬币,最少需要多少个硬币来找出K分钱的零钱。硬币面值中总是有面值为1的硬币。
第一行输入一组数据表示硬币面值,用空格分割,第二行输入要找的K分钱
输入数据:
1 5 10 21 25
63
输出数据:
3
看到这道题的瞬间,我是只想到了穷举算法(捂脸),所以想出了下面这样的写法:
1 public class MakeChange { 2 private static int min = Integer.MAX_VALUE; 3 /** 4 * @param coins 硬币数组 5 * @param k 要找的零钱k 6 * @param count 当前所用的硬币数 7 * @return 8 */ 9 public static void take_change(int[] coins,int k,int count){ 10 if(k<0){ 11 return; 12 } 13 if(k==0){ 14 if(min>count) 15 min = count; 16 } 17 for(int coin:coins){ 18 take_change(coins,k-coin,count+1); 19 } 20 } 21 }
就是很普通的遍历硬币数组,然后每个硬币都试一下,当要找的零钱k等于0的时候,说明这次找到的是一次有效的组合,然后观察此时的硬币数量count跟当前最小值的硬币数量的大小关系,最后运算完毕min自然是需要的最少硬币数了。但这个算法的运行时间极其的长,光是运行测试样例中的数据就已经接近1s了,并且随着数组长度的增加,时间会增长的特别快,所以这个算法不可行~~~这个时间复杂度我不会算(苦笑)
这时,回归题目本身,我试图从从微小的例子中看有木有规律,于是做了下面这张表:
硬币数组: 1 5 10 21 25
要找零的零钱:1 2 3 4 5
硬币数: 1 2 3 4 1
发现手工运算中,在要找的零钱为5时发生了变化,因为硬币数组里面有5这个面值,那么,如果以dp[i]表示找1分钱需要的最少的硬币数,那么在i=5时(即要找的零钱数为5),其实是隐含了dp[5] = min(dp[5-1]+1,dp[5-硬币数组中的面值]+1,这个算式的,抽象一下就是:
dp[i] = min(dp[i-1]+1,dp[i - for i in coins]+1)
解释一下意思,找i元零钱最少需要的硬币=min(找i-1元零钱最少需要的硬币+1枚硬币(相当于增加了一枚一元硬币),找i-coin元零钱最少需要的硬币+1,其中coin为可用的硬币面值,注意i-coin可能是负数)
因此,只需要从0元零钱一直递推到题目需求的k元零钱就能解决问题,时间复杂度为O(AN),其中A是数组的长度,严格来说其实就是O(N)
代码如下:
1 public static void main(String args[]) throws NumberFormatException, IOException{ 2 BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 3 String num = in.readLine(); 4 int length = Integer.parseInt(num); 5 String[] str = in.readLine().split(" "); 6 int[] coins = new int[str.length]; 7 for(int i=0;i<coins.length;i++){ 8 coins[i] = Integer.parseInt(str[i]); 9 } 10 11 for(int i=0;i<coins.length;i++){ 12 System.out.println("coins["+i+"]"+coins[i]); 13 } 14 int[] dp = new int[length+1]; 15 dp[0] = 0; 16 for(int i=1;i<=length;i++){ 17 int min = dp[i-1]+1; 18 for(int coin:coins){ 19 if(i-coin>=0){ 20 min = Math.min(min, dp[i-coin]+1); 21 } 22 } 23 dp[i] = Math.min(dp[i-1]+1,min); 24 } 25 System.out.println(dp[length]); 26 }
写的不太好请读者们见谅~~~目前还在努力学习技术和增强自己的表达方式中,想通过写文章来锻炼自己~~~