给出面值数组arr,其中的值都是正数且无重复。每个值都认为是一种面值,并且张数是无限的,现给出正数aim,求返回组成aim的方法数。

问题描述:给出面值数组arr,其中的值都是正数且无重复。每个值都认为是一种面值,并且张数是无限的,现给出正数aim,求返回组成aim的方法数。

例如:arr = {1,2},aim = 4

组成方法如下:1+1+1+1、1+1+2、2+2,一个三种方法,因此返回 3

解决方案:从暴力递归到动态规划的解决思想。

1、暴力递归的解决方法,核心代码如下:

/**
 * @author wanghuainan
 * @date 2021/7/8 11:12
 */
public class NanDaoCoinsWayNoLimit {

    public static void main(String[] args) {
        int[] arr = {1,2,3};
        int aim = 3;
        int ans1 = coinsWayNoLimit(arr, aim);
    
        System.out.println("ans1:"+ ans1);
    }
 

    /**
     * 暴力递归方案
     * @param arr
     * @param aim
     * @return
     */
    private static int coinsWayNoLimit(int[] arr, int aim) {
        if(arr == null || arr.length == 0 || aim < 0){
            return 0;
        }
        return processNoLimit(arr,0 ,aim);
    }

    private static int processNoLimit(int[] arr, int index, int rest) {
        if(index == arr.length){//已经到了最后一位,没钱了
            return rest == 0 ? 1 : 0;
        }
        int ways = 0;
        for(int zhang = 0;zhang * arr[index] <= rest; zhang++){
            ways += processNoLimit(arr,index + 1,rest - (zhang * arr[index]));
        }
        return ways;
    }

}

2、动态规划的思想

2.1、二维数组图示如下:

arr = {1,2,3};aim = 3;int N = arr.length;

 int[][] dp = new int[N + 1][aim + 1];?号位置dp[0][aim]就是返回的结果;左下角dp[N][0] = 1 是默认值;从第N行依次向上计算值。

2.2、核心代码如下:

/**
 * @author wanghuainan
 * @date 2021/7/8 11:12
 */
public class NanDaoCoinsWayNoLimit {

    public static void main(String[] args) {
        int[] arr = {1,2,3};
        int aim = 3;
        int ans2 = dpNoLimit1(arr, aim);
        System.out.println("ans2:"+ ans2);
    }
 
    /**
     * 动态规划方案
     * @param arr
     * @param aim
     * @return
     */
    private static int dpNoLimit1(int[] arr, int aim) {
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 1;//和为0时,无论数组中有多少值,只有一种方法。
        for(int index = N -1; index >= 0; index--){//从N-1行往上加
            for(int rest = 0;rest <= aim; rest++){//从左到右计算
                int ways = 0;
                for(int zhang = 0;zhang * arr[index] <= rest;zhang++){
                    ways += dp[index + 1][rest - (zhang * arr[index])];
                }
                dp[index][rest] = ways;
            }
        }
        return dp[0][aim];
    }

}

3、优化后的动态思想,核心代码如下:

/**
 * @author wanghuainan
 * @date 2021/7/8 11:12
 */
public class NanDaoCoinsWayNoLimit {

    public static void main(String[] args) {
        int[] arr = {1,2,3};
        int aim = 3;
        int ans3 = dpNoLimit2(arr, aim);
        System.out.println("ans3:"+ ans3);
    }

    /**
     * 优化后的动态规划方案
     * @param arr
     * @param aim
     * @return
     */
    private static int dpNoLimit2(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] = 1;//和为0时,无论数组中有多少值,只有一种方法。
        for(int index = N -1; index >= 0; index--){//从N-1行往上加
            for(int rest = 0;rest <= aim; rest++){//从左到右计算
                dp[index][rest] = dp[index + 1][rest];
                if(rest - arr[index] >= 0){
                    dp[index][rest] += dp[index][rest - arr[index]];
                }
            }
        }
        return dp[0][aim];
    }

  
}

到此,改算法的解决方案分享完毕,写一篇我们继续分析类似的一道算法,敬请期待!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

寅灯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值