找零问题
问题描述:
给定总金额为n(n为正整数)和若干面值为d1=1<d2<d3<...<dj...<dm的硬币(硬币数量无限),求用最小的硬币数凑足金额n的方案。
注:如果n<d1,那么无法用现有的硬币面值找零,为规避这种情况,假定n>=d1;d1=1能够保障任何正整数n都能被准确的找零。
分析:
为了验证其最优子结构的性质,可以这样考虑:
为了求解问题规模为n的最小硬币数量C(n),向前思考一步,C(n)实际上可以认为是从问题规模为n-dj(满足d1<=dj<=n)的基础上在选取面值为dj的硬币而来。求解C(n)只需要求解所有满足条件的子问题n-dj中最小的C(n-dj),那么,C(n)在最小的C(n-dj)基础上加1即可。
其递推关系为:
动态规划实现:
/* * 动态规划实现 * */ public static int changeOfLessCoins(int[] ary,int n){ int m = ary.length; int[] aux = new int[n+1];//aux[0]充当哨兵,没事实际含义 int tmpLest; for (int i = 1; i <= n; i++) { tmpLest = Integer.MAX_VALUE; for (int j = 0; j < m; j++) { if (i>=ary[j]) { tmpLest = Math.min(aux[i-ary[j]], tmpLest);//目的在于找出一个最小的tmpLess } } aux[i] = tmpLest+1; } return aux[n]; }
贪心算法实现:
/* * 贪心算法实现 * */ public static int changeOfLessCoins(int[] ary,int n){ int length = ary.length; int count = 0; while(n>0){ for (int i = length-1; i >= 0; i--) {//每次从最大的面值开始选择 if (ary[i]<=n) { n -= ary[i]; count++; break; } } } return count; }
与找零问题类似的还有像完美平方问题:
给定一个正整数N,用最少的平方数(1,4,9,26,25...)使得其和等于N。
比如N=13,最少的平方数为13=4+9,只需要4和9两个平方数即可。
参考资料:
算法按设计与分析基础.第三版.第十五章