动态规划
动态规划问题的特征
- 最优子结构
问题的最优解包含的子问题的解也是最优的 —— 从子问题的最优解推出问题的最优解 - 无后效性
当前的状态值一旦确定,则后续的状态的确定只和这些状态的值有关,而和到达这些状态的路径无关(这样才可能保存状态,且状态推演)
动态规划一般步骤
- 分解子问题
子问题和原问题类似,且需要保存子问题的解 - 确定状态
通常情况下,k个整形变量构成一个状态,用k维整型数组表示状态。整个问题的时间复杂度就是状态数目乘以计算每个状态所需要的时间 - 确定一些初始状态(临界状态)的值
- 确定状态转移方程
从一个或多个“值”已知的状态,求出另一个状态的值 —— “人人为我”
求出一个状态的“值”之后,更新该状态可能影响到的其他状态的“值” —— “我为人人”
动态规划的三种形式
- 记忆递归型
用全局数组记录各问题的解,用递归函数的形式求解总问题,递归到子问题时,先看是否已经求解该子问题并记录:如果已经求解,则直接返回记录的值,否则求解,并记录。 - “我为人人”递推型
求出一个状态的“值”之后,更新该状态可能影响到的其他状态的“值” - “人人为我”递推型
从一个或多个“值”已知的状态,求出另一个状态的值。
这种方式相比于2,可以采用某些数据结构来选取已经获得的状态中的最优值,而不需要对上一阶段的所有状态进行遍历
动态规划规律
状态选择要详尽、简洁:
搜索问题、动归问题,要选择一个合理的“状态”作为每个搜索/递推的节点,该状态要能够表达问题在每一步骤的全部的有用信息,但也要尽量简洁。
状态细化:
当选取的状态难以进行递推时(常见于分解出的子问题和原问题一样,但不具有无后效性),考虑将状态增加限制条件后分类细化,即增加维度,然后在新的状态上尝试递推。
其他
动态规划有时需要对空间进行优化,例如可以使用滚动数组来代替二维数组。
滚动数组(可以循环利用的一维数组),例如用一维数组模拟二维数组,这要求第n行的数据只在求解第n+1行时用到,在求第n+2,n+3...时都用不到,这样才不用持续保存,可以滚动更新。
有时动态规划的状态比较复杂,看上去需要很多空间,比如一个多维数组才能表示一个状态,这时,可以对状态进行编码,进行压缩后表示。
比如状态和某个集合有关,集合里有一些元素,没有另一些元素,那么可以用一个整数表示该集合,每个元素对应一个bit。