动态规划(DP)思想:
通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题
和最优子结构性质的问题
。
动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息
。(在分治思想中,前一子问题和后一子问题互不影响)
在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解
,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
注意:
由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。
DP的理解:
怎么鉴定DP可解的一类问题需要从计算机是怎么工作的说起…
计算机的本质是一个状态机,内存里存储的所有数据构成了当前的状态,CPU只能利用当前的状态计算出下一个状态(不要纠结硬盘之类的外部存储,就算考虑他们也只是扩大了状态的存储容量而已,并不能改变下一个状态只能从当前状态计算出来这一条铁律)
当你企图使用计算机解决一个问题是,其实就是在思考如何将这个问题表达成状态(用哪些变量存储哪些数据)以及如何在状态中转移(怎样根据一些变量计算出另一些变量)。所以所谓的空间复杂度就是为了支持你的计算所必需存储的状态最多有多少,所谓时间复杂度就是从初始状态到达最终状态中间需要多少步!
非波那契数例子理解:
我想计算第100个非波那契数,每一个非波那契数就是这个问题的一个状态
,每求一个新数字只需要之前的两个状态。所以同一个时刻,最多只需要保存两个状态,空间复杂度就是常数;每计算一个新状态所需要的时间也是常数且状态是线性递增的,所以时间复杂度也是线性的。上面这种状态计算很直接,只需要依照固定的模式从旧状态计算出新状态就行(a[i]=a[i-1]+a[i-2])
,不需要考虑是不是需要更多的状态,也不需要选择哪些旧状态来计算新状态。对于这样的解法,我们叫递推
。
非波那契那个例子过于简单,以至于让人忽视了阶段的概念
,所谓阶段是指随着问题的解决,在同一个时刻可能会得到的不同状态的集合。非波那契数列中,每一步会计算得到一个新数字,所以每个阶段只有一个状态。
围棋棋盘例子理解:
想象另外一个问题情景,假如把你放在一个围棋棋盘上的某一点,你每一步只能走一格,因为你可以东南西北随便走,所以你当你同样走四步可能会处于很多个不同的位置。从头开始走了几步就是第几个阶段,走了n步可能处于的位置称为一个状态,走了这n步所有可能到达的位置的集合就是这个阶段下所有可能的状态。
这个问题中走n
步可以走到很多位置一样。但是同样n
步中,有哪些位置可以让我们在第n+1
步中走的最远呢?没错,正是第n
步中走的最远的位置。换成一句熟悉话叫做"下一步最优是从当前最优得到的"
。所以为了计算最终的最优值,只需要存储每一步的最优值即可,解决符合这种性质的问题的算法就叫贪心。如果只看最优状态之间的计算过程是不是和非波那契数列的计算很像?所以计算的方法是递推。
既然问题都是可以划分成阶段和状态
的。这样一来我们一下子解决了一大类问题:一个阶段的最优可以由前一个阶段的最优得到。
一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!
每个阶段只有一个状态->递推;
每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心;
每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到,这个性质就叫做最优子结构;
不管之前这个状态是如何得到的,这个性质就叫做无后效性。
摘自:
https://www.zhihu.com/question/23995189
动态规划和分治思想的异同处:
相同:
将问题规模变小,变成若干个子问题,该子问题具有最优子结构。
不同:
动态规划的子问题具有重叠性,一个子问题在下一阶段决策中可能被多次使用到,即后面的问题可能依赖于前面的问题;分治中所有的子问题相互独立,互不影响。
动态规划的常见类型:
矩阵型
序列型
双序列型
划分型
区间型
背包型
状态压缩型
树型其中,在技术面试中经常出现的是矩阵型,序列型和双序列型。划分型,区间型和背包型偶尔出现。状态压缩和树型基本不会出现(一般在算法竞赛中才会出现)。