0
点赞
收藏
分享

微信扫一扫

动态规划 ——用数组记录中间过程的递归

好的吧,其实我是个渣渣,非科班出身, 还是这两个月刷leetcode的时候接触到的这个概念。然后看了群友推荐的一套动态规划的讲解视频。还有几个被推荐的讲的比较好的视频,另外加上看了n久百度,leetcode刷了好几道动态规划的题目的。最终决定系统的,从头开始学一遍动态规划并且记录成笔记。
先说一个众所周知的我理解的动态规划是什么?

动态规划——用数字记录中间过程的递归

斐波那契数列

要先从斐波那契数列开始说起。
其实如果从最开始说起的话,为什么叫这个名字我不清楚。但是理解这个斐波那契数列是从一个题目开始的。
题目是爬楼梯
一次可以爬一节楼梯也可以爬两节楼梯。现在有n节楼梯问一共有几种爬楼梯的方式!
其实一开始是很懵逼的,想了很久也没想出来,结果看了题解才明白这道题应该怎么想。
首先,

  • 如果有1节楼梯,那么只能有一种方式爬。
  • 如果2节楼梯,可以1 1爬也可以直接2爬,就是两种方式。
  • 如果有三节楼梯,可以看作(有1节楼梯的所有方式+2节楼梯。)+(两节楼梯的所有方式+1节楼梯)。
    这个思路要是懂了,这道题其实也就迎刃而解了。因为接下来是一样的。
  • 如果有四节楼梯,可以看作(三节楼梯的所有方式+1节楼梯)+(二节楼梯的所有方式+2节楼梯)
  • 如果是5节楼梯,则是(四节楼梯的所有方式+1节楼梯)+(三节楼梯的所有方式+2节楼梯)
  • 到了这里其实就能看出递归关系了。第n(n要大于2,不然前面没两节可加的)节楼梯就是(n-1节楼梯所有方式+1节)+(n-2节楼梯所有方式+2节)
    这个其实就是典型的斐波那契数列。


    视频截图中有一个单词:overlap sub-problem.这个单词的意思是重叠子问题。其实这个单词我个人感觉就是递归的标志。
    而不管是斐波那契数列,还是动态规划(这两个的区别以后再解释)都是典型的递归问题!
    而如果单纯的电脑计算斐波那契数列,正着算则时间复杂度是O(N方)(我不会打n方,就直接文字表示了,n的平方)
    因为想知道6,要先计算5,4.而其实计算5的时候已经计算了一遍4了,可是不人为操作电脑是不会直接引用4的答案的,而是再计算一边。
    解决这个问题的办法就是反着算:
    图中上面的计算时间复杂度就是n方,而下面的就是n。

其实我这么说可能很费解,我自己也觉得没讲师说的明白。反正只要知道咱们想知道n往回倒需要n方的时间复杂度。这也就是递归的写法。
而我们想知道n,从1一直算到n只需要n的时间复杂度。这个就是斐波那契数列的算法,也可以算是动态规划的算法。

求最大值,最优解

这是动态规划中一种很经典的题目。就是求最大值,最优解。我把题目贴出来:


如图,横轴代表时间,而每一个条条上的数代表收益。假如有一个人,同一时间只能做一件事。如何选取能让这个人在0到11点赚最多的钱?

我感觉题意我描述的很明白了。比如这个人接了第一个任务,那么1点到四点之间不能做别的了。也就不能接第2个任务,第3个任务,第5个任务。
同理如果接了第二个任务,就不能接第三个,第四个,第五个。因为时间冲突。
现在问的是如何赚最多的钱。也就要用到动态规划:
首先,我们设置一个函数OPT(i)。假设这个OPT(i)就是i个元素的最优解。
现在我们继续讲对每一个任务来说,只有两种状态:

不接

我们拿第八个做比喻:
如果刚刚图中任务8,我们接了,那么钱数就是 任务8的钱数4 + OPT(5)的钱数。因为任务8和任务6,7时间冲突,所以接了任务8就不能接任务6,7了。
如果刚刚人任务8我们不接,那么就没有8的4块钱,但是没有时间冲突了,得到的钱数就是OPT(7)
一直到这里都还比较容易理解吧?而这个思路整理成公式就是下图:

而图中这个prev(i)指的是选择i的话,上一个可选择的是几。继续拿这道题举例子:

  • 如果选择了任务8,那么上一个能接的任务是5,这个prev(8)=5
  • 如果选择了任务7,那么上一个能接的任务是3,这个prev (7) =3
  • 如果选择了任务6,那么上一个能接的任务是2, 这个prev(6) = 2
  • 如果选择了任务5,那么上一个能接的任务是0, 这个prev(5) = 0
    以此类推。这里注意一下,这个prev是要计算的。



    一直到这里,这个求最大值问题就变成了一个递归的重叠子问题,如下图所示:



    就如上面讲的,如果我们用递归实现,那么就会不断计算同样的值,n方的时间复杂度。
    而如果使用动态规划从下往上算,则不用做那么多无用的计算。
接下来咱们说另一个例子。

这个是在视频中讲师举的例子,但是我在leetcode中刷题也遇到过类似的题目。就是打家劫舍这道题。

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

就如上面这一道题。不能连续偷,如何能使偷盗金额最大?其实这道题是比上上一个做工挣最多钱的题目要简单的。因为这道题的prev是很明确的就是上一个。比如偷与不偷第八个。只取决于opt(7) 和opt(6)+val8哪个大就行了。
附上一张视频中的解题思路:


其实单纯的理解思想不难,难的是把这种思想在实际中自己用出来。重点是自己。
说一个重要的概念:递归出口

递归出口

每一个递归都是要有出口的,不然就是死递归了。而如何找这个出口呢?拿上面的题目做例子,什么时候可以停止递归计算?
1,当只有一个元素的时候,因为肯定是非负数,所以肯定是选的,也就是opt(1) = val1;
如果有两个元素,只会选择较大的那个,也就是opt(2)=Math.max(val1,val2).
这就是递归出口。
递归出口能方便我们找出解题的思路,而动态规划我们一般不用递归方式写。
说到这我还要说一遍上面提到的正着算(递归)和反着算的区别,就拿这道题“打家劫舍”来说,如果用递归的方法时间复杂度n方。执行结果就是超时,如图:



如上图所示,代码是没问题的,但是运行报错超时,这个时候如果我们换回动态规划的思路,正着算,从1一直计算到i就是n的时间复杂度:


如图,用动态规划解题就一次过了,而且性能还贼好。这里说一下因为动态规划要有两个起始点(第一个和第二个)其实这里有两种做法,一种是直接把数组中的第一个正确值和第二个正确值写进来,这样p的i和数组的i也就一样了,遍历从2开始。但是我嫌麻烦,所以是第1,2个记作0,然后p的下标和数组下标多了2.
反正这个理解了就行,
另外因为我是java的,所以写的demo也是java,但是视频中讲师是python。所以也附上pyth的代码(在讲师的讲解下大概知道代码是干什么的,但是我完全不会python):


拼凑数字

这个题目在leetcode我还没做到过。只做过类似的树或者数组种两个数的和是不是某值。
但是讲师讲了,所以我这里也照猫画虎一下。

题目:给定一个数组。再给定一个数值。这里规定的是数组种元素都是非负数,给定的数值也是非负数。判断数组中是否若干元素相加可以拼凑出给定数值。

其实两个数字可以单纯的减法,但是多个数值就不能生算了。我觉得可能是讲师出题的时候没考虑那么多,或者说故意为之,反正这个题的解法思路是递归,然后从第一/最后一个元素开始一个个元素计算。每个元素分两种情况:选,不选。

  • 选的话,给定值变为给定值减去当前值。
  • 不选的话,给定值不变。
    然后还有2.5种出口:
  1. 已经遍历完一遍数组(如果从后往前遍历的遍历到arr[0],从前往后遍历是遍历到arr[len-1]),则直接return arr[0/len-1]==s就可以了。
  2. s等于0,则说明这个数已经拼凑完了,直接返回true就行了。
  3. 这是半个出口,因为要求都是非负数,所以当有元素大于给定s了则这个数肯定不选,所以继续往前判断就行了。

附上相应的讲师代码:



然后改考虑用非递归要怎么实现?这里要重复一下,动态规划就是用数组记录中间过程。所以第一版本先用递归实现方便我们理解问题,找出解决问题的思路。而转而用动态规划(非递归)实现是为了更优的性能。

接下来我们说非递归实现的(感觉其实也用到了递归,只不过计算次数较小而已)。
再次说明动态规划本质就是用数组记录中间过程。而这道题的中间过程是一个二维数组,因为每一个元素对应多种情况。所以这道题首先是要脑补或者画出一个二维数组,这个二维数组就是能不能凑出来的情况。
(我尽量用我的渣画技表达明白)


代码如下图所示,最终能选择到绿色块块说明是可以拼凑出来的,选不到说明拼不出来。


看看图,看看代码,你品,你细品~~反正品不出来就多看看,画画图,寻思寻思。
讲师讲的比我还简洁呢,我反正是听明白了,就不知道说没说明白。
至于这个动态规划和上面递归的区别,其实是递归次数的本质差别,我反正是用debug模式来回跑了几遍才有的深刻认识。
建议如果自己一步步跟代码走那么demo中的数组元素多设计一些,这样才能明显的看出区别。
然后这个第一章动态规划的笔记就记到这里,一点不夸张的说这一篇文章我写了一个星期才写完,一点点学,一点点了解,再记笔记。不是不能一口气学完写完,但是感觉那样印象不深,现在属于一天学一点,没事还寻思寻思,哈哈
如果这篇文章稍微帮到你了记得点个喜欢点个关注。最近在学习算法,也希望能找到一起学习进步的朋友~另外如果文中某些措辞或者理解不严谨或者有问题欢迎指出,也祝大家工作顺顺利利!

举报

相关推荐

0 条评论