23.2:货币问题之最少货币数问题

arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。
每个值都认为是一种面值,且认为张数是无限的。
返回组成aim的最少货币数

暴力解法

public static int minCoins(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim <= 0) {
        return 0;
    }
    return process(arr, aim, 0);
}

//从左往右,进行选择,返回正好组成aim的最少货币数。
//用 Integer.MAX_VALUE表示搞不定。
public static int process(int[] arr, int aim, int index) {
    if (index == arr.length) {
        return aim == 0 ? 0 : Integer.MAX_VALUE;
    }
    //coins是收集可以搞定的情况下,最少货币张数。
    int coins = Integer.MAX_VALUE;
    //当前位置的货币拿几张。
    for (int zhang = 0; aim - zhang * arr[index] >= 0; zhang++) {
        int p = process(arr, aim - zhang * arr[index], index + 1);
        //只有在可以搞定的情况下才可以计算最少货币的张数。
        if (p != Integer.MAX_VALUE) {
            coins = Math.min(coins, zhang + p);
        }
    }
    return coins;
}

也可以是如下

	public static int minCoins(int[] arr, int aim) {
    	if (arr == null || arr.length == 0 || aim <= 0) {
        	return 0;
    	}
    	return process(arr, aim, 0);
	}

	public static int process2(int[] arr, int aim, int index) {
        if (index == arr.length) {
            return aim == 0 ? 0 : -1;
        }
        int ans = Integer.MAX_VALUE;
        for (int zhang = 0; aim - zhang * arr[index] >= 0; zhang++) {
            int p = process2(arr, aim - zhang * arr[index], index + 1);
            //当for循环的条件被打破时,向上层返回的将是Integer.MAX_VALUE,上层P接收到后,如果没有如下条件限制直接进			   //行zhang + p就会越界。
            //解释一下如何打破for循环 ->当最后一层的张数不断增加但是一直凑不齐整后,就会发生				   			   //aim - zhang * arr[index] < 0
            if (p != -1 && p != Integer.MAX_VALUE) { //关键点
                ans = Math.min(zhang + p, ans);
            }
        }
        return ans;
    }

在这里插入图片描述

public static int dp1(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim <= 0) {
        return 0;
    }
    int N = arr.length;
    int[][] dp = new int[N + 1][aim + 1];
    dp[N][0] = 0;
    for (int index = 1; index <= aim; index++) {
        dp[N][index] = Integer.MAX_VALUE;
    }
    for (int index = N - 1; index >= 0; index--) {
        for (int aimIndex = 0; aimIndex <= aim; aimIndex++) {
            int coins = Integer.MAX_VALUE;
            //当前位置的货币拿几张。
            for (int zhang = 0; aimIndex - zhang * arr[index] >= 0; zhang++) {
                int p = dp[index + 1][aimIndex - zhang * arr[index]];
                //只有在可以搞定的情况下才可以计算最少货币的张数。
                if (p != Integer.MAX_VALUE) {
                    coins = Math.min(coins, zhang + p);
                }
            }
            dp[index][aimIndex] = coins;
        }
    }

    return dp[0][aim];
}

DP方法

	public static int dp1(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim <= 0) {
            return 0;
        }
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 0;
        for (int index = 1; index <= aim; index++) {
            dp[N][index] = Integer.MAX_VALUE;
        }
        for (int index = N - 1; index >= 0; index--) {
            for (int aimIndex = 0; aimIndex <= aim; aimIndex++) {
                int coins = Integer.MAX_VALUE;
                //当前位置的货币拿几张。
                for (int zhang = 0; aimIndex - zhang * arr[index] >= 0; zhang++) {
                    int p = dp[index + 1][aimIndex - zhang * arr[index]];
                    //只有在可以搞定的情况下才可以计算最少货币的张数。
                    if (p != Integer.MAX_VALUE) {
                        coins = Math.min(coins, zhang + p);
                    }
                }
                dp[index][aimIndex] = coins;
            }
        }

        return dp[0][aim];
    }

dp方法进一步优化(斜率优化)

在这里插入图片描述

	public static int dp2(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim <= 0) {
            return 0;
        }
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 0;
        for (int index = 1; index <= aim; index++) {
            dp[N][index] = Integer.MAX_VALUE;
        }
        for (int index = N - 1; index >= 0; index--) {
            for (int aimIndex = 0; aimIndex <= aim; aimIndex++) {
                dp[index][aimIndex] = dp[index + 1][aimIndex];
                //暴力递归中已经明确写好了,要计算货币张数时必须保证格子是yo
                //现有这个格子,并且这个格子的值是有效的
                if (aimIndex - arr[index] >= 0 && dp[index][aimIndex - arr[index]] != Integer.MAX_VALUE) {
                    //为什么 dp[index][aimIndex - arr[index]] + 1 后面写着。
                    dp[index][aimIndex] = Math.min(dp[index][aimIndex - arr[index]] + 1, dp[index][aimIndex]);
                }
            }
        }
        return dp[0][aim];
    }

注意:

注意这是dp方法中的核心方法。

		for (int index = N - 1; index >= 0; index--) {
            for (int aimIndex = 0; aimIndex <= aim; aimIndex++) {
                int coins = Integer.MAX_VALUE;
                //当前位置的货币拿几张。
                for (int zhang = 0; aimIndex - zhang * arr[index] >= 0; zhang++) {
                    int p = dp[index + 1][aimIndex - zhang * arr[index]];
                    //只有在可以搞定的情况下才可以计算最少货币的张数。
                    if (p != Integer.MAX_VALUE) {
                        coins = Math.min(coins, zhang + p);
                    }
                }
                dp[index][aimIndex] = coins;
            }
        }

会发现待填格子p在填写之前都会求其依赖的格子+zhang数的最小值,所以画图的时候一定要对着代码分析全面了。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙-橙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值