本文整理转载自:知乎什么是动态规划
什么是动态规划
动态规划(dynamic programming)与分治法相似,都是通过组合子问题的解来求解原问题,动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题。通常按照下面4个步骤来设计一个动态规划算法:
1. 刻画一个最优解的结构特征。
2. 递归地定义最优解的值
3. 计算最优解的值,通常采用自底向上的方法
4. 利用计算出的信息构造一个最优解
这是算法导论上的定义,但是这个定义对于初学者来说并没有很大的帮助,因为不知道如何划分子问题和刻画最优解的结构
1.动态规划的本质
动态规划的本质是对问题的状态的定义和转态转移方程的定义
动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。但如何拆分问题才是动态规划的核心,而拆分问题靠的是状态的定义和状态方程的定义。
1.1 状态的定义
现举一个列子:
给定一个数列,长度为N,
求这个数列的最长上升(递增)子数列(LIS)的长度.
以
1 7 2 8 3 4
为例。
这个数列的最长递增子数列是 1 2 3 4,长度为4;
次长的长度为3, 包括 1 7 8; 1 2 3 等.
要解决这个问题首先我们需要定义这个问题和这个问题的子问题。
所以我么重新定义这个问题
给定一个长度为N的数列。
设
Fk
为:以数列中第k项结尾的最长递增子序列的长度.
求
F1...FN
的最大值
很显然这个问题与原问题是等价的。
而对于
Fk
来说,
F1
..
Fk−1
都是
Fk
的子问题:因为以第k项结尾的最长递增子序列(简称LIS),包含着以第1 … k 项结尾的LIS。
上述问题中
Fk
称之为状态,定义中的”
Fk
为数列中第k项结尾的LIS的长度”就叫做状态的定义。之所以把
Fk
叫做”状态”而不是”问题”,一是因为避免与原问题中”问题”混淆,二是因为这个新问题是数学化定义的。
当然,我们可以使用不同的视角来定义这个问题:
给定一个长度为N的数列,
设
Fi,k
为:
在前i项中,长度为k的最长递增子序列中,最后一位的最小值。
1≤k≤N
若在前i项中,不存在长度为k的最长递增子序列,则
Fi,k
为正无穷。
求最大的x,使得
FN,x
不为正无穷。
这个问题的定义与原问题也是等价的。
上述的
Fi,k
就是状态,定义中的“
Fi,k
为:在前项中,长度为k 的最长递增子序列中,最后一位的最小值”就是对状态的定义。
1.2 什么是状态转移方程?
上述状态定义好之后,转态和转态之间的关系式,就叫做状态转移方程
对于LIS问题,第一种定义:
设
Fk
为:以数列中第k项结尾的最长递增子序列的长度。
设A为题中数列,转态转移方程为:
F1=1
(根据定义导出边界情况)
Fk=max(Fi+1|Ak>Ai,i∈(1...k−1)(k>1)
文字解释为:
以第k项结尾的LIS的长度是:保证第i项小的情况下,以第i项结尾的LIS长度加1的最大值,取遍i的所有值(i小于k).
第二种定义:
设
Fi,k
为:在数列前i项中,长度为k的递增子序列中,最后一位的最小值
设A为题中数列,状态转移方程为:
若
Ai>Fi−1,k−1
则
Fi,k=min(Ai,Fi−1,k)
否则:
Fi,k=Fi−1,k
(边界情况需要分类讨论较多,在此不列出,需要根据状态定义导出边界情况。)大家套着定义读一下公式就可以了,应该不难理解,就是有点绕。
这里可以看出,这里的状态转移方程,就是定义了问题和子问题之间的关系。
可以看出,状态转移方程就是带有条件的递推式。