来源 | 昊天码字
责编 | Carol
封图 | CSDN 付费下载于视觉中国
碰到动态规划问题摸不着头脑?总结不出动态规划的类型?有多少人曾经历过这种迷茫与无助?看完本文,让你一脚迈进动态规划的大门。
我们在用递归求解问题的过程中,可能会存在将递归的某一环节计算多次的现象,因为之前的递归结果无法有效存储,下次碰到一样的值还需要重新计算一次,下面引出一个例子:
相信大家都知道斐波那契数列,那我们由斐波那契数列递归求解过程中带来的问题来引出动态规划问题。
斐波那契数列
斐波那契数列的规律为:当n > 2时,fib(n)=fib(n-1)+fib(n-2),当n为1或2时,fib(n)=1。假如我们让电脑代入这个递归式来求fib(5),则求解过程如下图:
求fib(5)
我们可以看到,如果我们按照如上的递归式来求一个斐波那契数,那么当n很大时,这棵树会非常庞大,且从中我们也可看出,电脑会非常傻的把fib(3)算了两遍,这种问题叫做重叠子问题,所以这个算法的时间复杂度会达到O(2^n),但实际上我们没有必要把fib(3)算两遍,我们在算第一遍时把它保存起来,在下次需要时直接调用就可以了。
因此我们在计算fib(5)时,完全可以从fib(1)开始计算,从1一直算到5,并不断保存,在必要时调用之前保存的值,而不需要重新运算,这样算法的时间复杂度瞬间就降到O(n)了。
所以我们在做动态规划时,首先要用到的就是这个思想,可以看出,动态规划的特点之一就是开辟内存保存数值,那么我们可以根据开辟内存的方式分为一维动态规划问题和二维动态规划问题,下面我们来具体介绍。
第一类问题(一维)
1.求薪水问题
我们来看下面的图,从上到下依次为8种工作,横轴代表工作所占的时间段,红色字体代表每份工作的薪水,两份同一时间的工作不能同时做,我们求的问题是一个员工怎样在0到11的时间段内获取最大的薪水。
我们首先思考这道题的本质,就是选不重合的工作,求能最多得到的薪水,我们先建立一个数组workvalue来保存每份工作的薪水。
我们先按照开辟内存的思想来做这道题,之后我会说明为什么要开辟内存。由上文的动态规划规律可知,我们可以依次从只做前一个工作能得到的最大薪水、只做前二个工作能得到的最大薪水、只做前三个工作能得到的最大薪水来思考,并建立一个value数组来保存它们,假如我们想求前6个工作中选哪些工作才能赚的最多,那么我们可以有两个选择:做或者不做这份工作,第一种,假如我们选择第6份工作,我们先