找零问题

问题描述:

给定总金额为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两个平方数即可。

参考资料:

算法按设计与分析基础.第三版.第十五章

原文地址:https://www.cnblogs.com/qcblog/p/7784080.html