[动态规划] 什么是动态规划

参考:

  1. 什么是动态规划?动态规划的意义是什么?(转自知乎)
  2. 什么是动态规划(Dynamic Programming)?动态规划的意义是什么? - 阮行止的回答 - 知乎 https://www.zhihu.com/question/23995189/answer/613096905

公式符号:

  1. $\left \lceil \right \rfloor $

本文可以顺便搞明白 “递推、贪心、搜索” 与 “动态规划”之间的关系。

动态规划是解决「某一类问题」

动态规划是对「某一类问题」的解决方法,重点在于如何鉴定 某一类问题 是动态规划可解的,而不是纠结于使用 「递归」还是 「递推」来解决。

如何鉴定这「某一类问题」示动态规划可解的呢?

答:还是要从计算机如何工作说起:计算机的本质是一个状态机内存里存储的所有数据构成了当前的「状态」,CPU只能利用当前的状态计算出下一个状态

当你试图使用计算机解决一个问题时,其实就是在思考如何将该问题表达成 状态(用哪些变量存储哪些数据)、以及 如何在状态中转移(怎样根据一些变量计算出另一些变量)。所以,所谓的:

  • $\left \lceil{空间复杂度} \right \rfloor $:就是为了支持你的计算所必须存储的状态最多有多少个;
  • $\left \lceil{时间复杂度} \right \rfloor $:就是从初始状态到达最终状态 需要走多少步;

状态 与 状态转移

比如说我想计算第100个非波那契数,每一个非波那契数就是这个问题的一个**「状态」**,由一个状态(斐波那契数A)转移到另一个状态(斐波那契数B)的过程 叫做 「状态转移」

根据斐波那契数列的公式我们知道,每求一个新数字只需要之前的两个状态。

  • 所以同一个时刻,最多只需要保存两个状态,空间复杂度就是常数;
  • 每计算一个新状态所需要的时间也是常数且状态是线性递增的,所以时间复杂度也是线性的。

上面这种状态计算很直接,只需要依照固定的模式从旧状态计算出新状态就行(a[i]=a[i-1]+a[i-2]),不需要考虑是不是需要更多的状态,也不需要选择哪些旧状态来计算新状态。对于这样的解法,我们叫==递推==。(递推与递归的区别,可见:递推 | 递归 | 迭代)

阶段

非波那契那个例子过于简单,以至于让人忽视了阶段的概念,所谓**==「阶段」==是指随着问题的解决,在同一个时刻可能会得到的不同状态的集合。**

非波那契数列中,每一步会计算得到一个新数字,所以每个阶段只有一个状态。

举一个其他例子,假如把你放在一个围棋棋盘上的某一点,你每一步只能走一格,因为你可以东南西北随便走,所以你当你同样走四步可能会处于很多个不同的位置。从头开始走了几步就是第几个阶段走了n步可能处于的位置称为一个状态走了这n步所有可能到达的位置的集合就是这个阶段下所有可能的状态

有了阶段之后,计算新状态可能会遇到各种奇葩的情况,针对不同的情况,就需要不同的算法,下面就分情况来说明一下:

贪心:一个阶段的最优可以由前一个阶段的最优得到

假如每个阶段有n个状态,每个阶段都有多个状态(不同阶段的状态数量不必相同)。一个阶段的一个状态可以得到下一个阶段的所有状态的几个。

所以,如果我们要计算出最终阶段的状态数 自然要经历之前每个阶段的某些状态。

如果是==「暴力算法」==:就相当于每个阶段的所有状态我们都需要经历,然后才得到最后阶段的所有状态。即 我们把所有的可能性都计算了一遍。

好消息是,有时候我们并不需要真的计算所有状态。举个例子:从棋盘的左上角到达右下角最短需要几步?

答:

某个阶段确实可以有多个状态,正如这个问题中走n步可以走到很多位置一样。但是同样n步中,有哪些位置可以让我们在第n+1步中走的最远呢?没错,正是第n步中走的最远的位置。即“下一步最优是从当前最优得到的”。

所以为了计算最终的最优值,只需要存储每一步的最优值即可,解决符合这种性质的问题的算法就叫==「贪心」==。

如果只看最优状态之间的计算过程是不是和非波那契数列的计算很像?所以计算的方法是递推

斐波那契数列 每个阶段只有一个状态,和贪心算法每个阶段只选择最优的那一个状态类似。

既然问题都是可以划分成阶段和状态的。这样一来我们一下子解决了一大类问题:一个阶段的最优可以由前一个阶段的最优得到。

动态规划:无后效性

接着上面的例子。如果一个阶段的最优 无法用前一个阶段的最优得到呢?

例如迷宫的例子,在计算从起点到终点的最短路线时,你不能只保存当前阶段的状态,因为题目要求你最短,所以你必须知道之前走过的所有位置。因为即便当前所在的位置不变,之前所走过的路线的不同 也是会影响之后走的路线的。

此时我们就需要保存 每个阶段所经历的状态,根据这些状态才能算出下一个状态。即之前所走的路线的每一步 都会 影响到下一步的选择,这种情况叫做 有后效性。对于这种情况,我们需要使用暴力算法。

但是呢,有一类问题,看似需要之前所有的状态,其实不用。以“最长上升子序列”为例子,来说明该例子为什么不必需要暴力搜索,进而引出动态规划的思路。

求解最长上升子序列:

答:

首先,从头到尾依次枚举是否选择当前的数字,每选定一个数字就要去看看是不是满足“上升”的性质,这里第i个阶段就是去思考是否要选择第i个数,第i个阶段有两个状态,分别是选和不选。

每次当我决定要选择当前数字的时候,只需要和之前选定的一个数字比较就行了!这是和之前迷宫问题的本质不同!

所以,我们可以不需要记录之前所有的状态(这个和 迷宫问题 不同)。

既然我们的选择已经不受之前状态的组合的影响了,那时间复杂度自然也不是指数了。此时,虽然我们不在乎某序列之前都是什么元素,但我们还是需要这个序列的长度的,只需要使用一个变量来记录某个元素结尾的LIS长度即可。

因此第i个阶段的最优解只是由前i-1个阶段的最优解得到的,然后就得到了DP方程:

L I S ( i ) = m a x { L I S ( j ) + 1 }      j < i   a n d   a [ j ] < a [ i ] LIS(i)=max\{LIS(j)+1\} \ \ \ \ j<i \ and\ a[j] < a[i] LIS(i)=max{LIS(j)+1}    j<i and a[j]<a[i]

所以,动态规划是 无后效性 的。

小结:

所以一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!

  • 每个阶段只有一个状态->「递推」
  • 每个阶段的最优状态都是由上一个阶段的最优状态得到的 -> 「贪心」
  • 每个阶段的最优状态是由之前所有阶段的状态的组合得到的->「搜索」
  • 每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->「动态规划」

其中,

  1. 每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到 ====该性质 叫做 最优子结构
  2. 而不管之前这个状态是如何得到的 ==== 这个性质叫做无后效性
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值