数学基础:三、动态规划2(求解凑齐钱的最小张数)

凑齐钱的最小张数概念:

比如只有2块、3块和5块钱若干,问凑齐100块钱最小需要几张钱能凑齐?(20张5块的,所以是20张)

前面一篇求解编辑距离时,有现成的状态转移方程,可是这种凑齐面值的没有现成的公式,只能自己去推导。

当然我们可以利用求余数求解,凑齐98块,需要98/5=19…3,所以为19张5块+1张3块(一共20张)

但这个可能用余数可能更方便,但对于动态归划方法,可能是个思路

比如只有2、3、7块,用表格推导的过程如下:
在这里插入图片描述
而如果只有2、5、9块,它的推导如下:
在这里插入图片描述
根据推导过程,写出的算法如下:

public class Lesson10_1 {
    // 钱币币种可选的值,自己可随意改
    private static final int[] coins = {2, 5, 9};

    // 如果不加变量保存,每次都要重复递归计算
    private static Map<Integer, Integer> hasCount = new HashMap<>();

    // 传入总金额和可选币种,返回凑够该金额的最小数量,返回null表示没有找到
    // 为null表示凑不齐这个钱(商场买菜要1块钱,可选币种最小值要2块,我们不能傻得给人家2块不找零,此时就是凑不齐)
    public static Integer getCount(int total, int[] coins) {
        // 如果之前已计算过该金额的,直接返回,无需递归调用了
        if (hasCount.containsKey(total)) {
            return hasCount.get(total);
        }
        // 递归减到了负数,也没凑齐,就是没找到
        if (total < 0) {
            return null;
        } else if (total == 0) {// 金额为0,那需要的数量肯定为0
            return 0;
        } else {
            int length = coins.length;
            // 保存可选币种中,每种所需的最小数量
            Integer[] min = new Integer[length];
            for (int i = 0; i < length; i++) {
                // 比如求凑20块钱,可选值是5块钱,那递归求凑15块+1即可    C20(5) = C15(5) + 1
                // 再比如求凑18块钱,可选值2块钱,那递归求凑16块+1即可    C18(2) = C16(2) + 1
                // C n (m) = C n-m (m) + 1
                Integer tmpResult = getCount(total - coins[i], coins);
                // 返回值为空,说明递归调用到 if (total < 0) 时返回了空,没有凑够该值
                min[i] = tmpResult != null ? (tmpResult + 1) : null;
            }
            // 去除空值
            List<Integer> resultList = Arrays.stream(min).filter(e -> e != null).collect(Collectors.toList());
            // 如果不为空,那最小值就是所需的最小个数
            Integer result = resultList.size() > 0 ? resultList.stream().mapToInt(e -> e).min().getAsInt() : null;
            // 放到map中,防止后续重复递归计算
            hasCount.put(total, result);
            return result;
        }
    }

    public static void main(String[] args) {
        Integer count = getCount(102, coins);
        System.out.println(count);
    }
}
原文地址:https://www.cnblogs.com/dulinan/p/12032997.html