1.递归到动规的一般转化方法
递归函数有n个参数,就定义一个n为值的逆过程的数组,数组的下标是递归函数参数的取值范围,数组元素的值是递归函数的返回值,这样就可以从边界值开始,逐步填充数组,相当于计算递归函数
类比数字三角形,从底部的一列开始,从下往上推,逐步填充数组。
由已知推未知,从递归到递推
2.动规解题的一般思路
2.1.将原问题分解为子问题
把原问题分解为若干个子问题,子问题和原问题形式相同或者类似,只不过规模变小了。子问题都解决,原问题即解决
(数字三角形为例,原问题是数字i,j到底边的最大和,子问题是它的正下方的数字到底边三角形的最大和或者右下方的到底边的数字最大和,这两个子问题如果都解决了,再加上自己就是原问题的解)
子问题的解一旦求出就会被保存,所以每个子问题只需求解一次。
2.2.确定状态
在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个状态,一个状态对应于一个或多个子问题,所谓某个状态下的值,就是这个状态下所对应的子问题的解。
(以数字三角形为例,子问题是求某一行某一列的数字到底边的最大和,与子问题相关的变量i,j就代表着状态,它对应的值就是这行这列到底边的最大和,有多少个数字,就有多少个状态。)
**所有状态的集合,构成问题的状态空间的大小,与动态规划解决问题的时间复杂度直接相关。**在数字三角形的例子里,一共有NX(N+1)/2个数字,所以这个问题的状态空间里一共就有NX(N+1)/2个状态
整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。
在数字三角形里每个状态只需要经过一次,且在每个状态上作计算所花的时间都是和N无关的常数。
用动态规划解题,经常碰到的情况是,K个整型变量能构成一个状态(如数字三角形中的行号和列号这两个变量构成状态)。如果这K个整型变量的取值范围分别是N1,N2….NK,那么,我们就可以用一个K维的数组array[N1][N2]…[Nk]来存储个状态的值。这个值未必就是一个整数或者浮点数,可能是需要一个结构才能表示的,那么array就可以是一个结构数组。一个状态下的值通常会是一个或多个子问题的解。
2.3.确定一些初始状态(边界状态)的值
以数字三角形为例,初始状态就是底边数字,值就是底边数字值
2.4.确定状态转移方程
定义出什么是状态,以及在该状态下的值后,就要找出不同的状态之间如何转移—即如何从一个或多个值已知的状态,求出另一个状态的值(人人为我递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作状态转移方程。
数字三角形的状态转移方程:
maxSum[r][j]的值为
D[r][j] r=N
max(maxSum[r+1][j],maxSum[r+1][j+1])+d[r][j]; 其他情况
3.能用动规解决的问题特点
1)问题具有最优子结构性质,如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质
2)无后效性。当前的若干状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前采取哪种手段或者经过哪条路径演变到当前的若干个这样的状态,没有关系。