动态规划练习三:换钱问题(动态规划概念理解与记忆搜索法概念理解对比)

题目描述

给定数组arr,数组中所有的值都为正数,且不为空不重复。数组中每个数代表一种货币的面值,每种面值的货币可以使用任意张,然后,给一个aim,代表要换钱的面值,请问,用数组中的面值换钱,总共有多少种方式?

暴力递归思路

递归的第一步,将问题划分为更小的子问题,比如,数组arr[1,5,10]这三种面值的货币,aim=1000。首先,可以先取出第一种面值。假如,第一种1元的有0张,那么问题就划分为arr=[5,10],aim=1000;如果第一种1元的有1张,问题就划分为arr=[5,10],aim=999。依此类推,直到第一种货币的面值一元有1000张时,也就是超过aim,那么,第一种的所有结果都例举出来了。
因此,从上面分析中抽象出递归函数。首先,递归函数需要记录arr,和目前是第几种货币面值,也就是数组的索引,其次,还需要记录aim,当aim为0时,就找到一种换钱方法。
递归的出口是,数组遍历完最后一个,此时aim恰好为0,说明找到一个换钱方法,不为零,就不是,当然,不对的情况包含aim大于和小于的情况。
递归方法,就是设定一个res记录当前符合的换钱方式,遇到出口就返回1或0,否则,就继续向下递归。
递归代码

    public static int process(int[] arr,int index,int aim){
        int res=0;
        if(index==arr.length){
            res = aim==0 ? 1:0;
        }else {
            for (int i = 0; i*arr[index]<=aim; i++) {
                res+=process(arr,index+1,aim-i*arr[index]);
            }
        }
        return res;
    }

记忆搜索法

暴力递归时间复杂度高的根本原因,就是存在大量的重复操作,而重复操作之所以大量发生,是因为在递归的过程中,很多重复计算的结果没有记录下来,才会导致重复计算。
因此,记忆搜索法其实也很简单,就是把每一步递归的结果放入一个map中,然后每次递归前,先检查map中有没有,如果已经有结果,直接取出来,就不需要重复计算了。

动态规划法

第一步,判断是否可行?本题中间递归结果是不依赖前面的路径的,因此可行。
第二步,画图,找出递归过程的变化量,index和aim,画图。
第三步,标记结果位置,此题的结果位置为index0,aim1000。
第四步,填入已知数据,已知数据为indexarr.length这一行,aim0的位置填返回值1,其他都是0。
第五步,根据已知结果,结合递归函数,我们已知了最后一行indexarr.length的结果,就可以根据递归函数,推出indexarr.length-1这一行的结果,然后依次填完表格,直到找到index0,aim1000位置的结果,就是返回的最终结果。

动态规划和记忆搜索对比

其实,动态规划可以理解为就是记忆搜索法 。
记忆搜索法不关心到达某一个递归过程的路径,只是单纯的记录递归过程的每个结果,避免重复计算。
动态规划是通过表格来表示每个递归过程之间的顺序,每个过程依赖前面已知的结果,其实也是在记录前面的递归结果,避免重复计算,只不过是也同时保留了递归顺序之间的过程。
两者都是空间换时间的优化思想,区别在于,动态规划记录计算顺序,而记忆搜索法只是简单避免重复计算,不记录计算顺序。

因此总结一下,什么动态规划?
其本质就是利用申请的额外空间来记录每一个暴力递归的结果,下次要用的时候可以直接使用,并且记录每种递归状态之间的计算顺序,依次计算。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值