分治、贪心、线性规划、动态规划、网络流为计算机学科常用的五大算法

大部分面试题都喜欢考动态规划,因为它不像线性规划、网络流算法那样直接套用,本身其没什么套路,都要现场分析现场建模,属于思维量大代码量小

现在尝试把这类问题用一个比较通用的模型给分析出来,以后再遇到这类问题希望可以直接套用

1. 动态规划(DP)的本质

动态规划介于贪心和NPC问题之间:它既不像贪心模型那样每一步都是最优解,也不像NPC问题那样每一步都不能确定只能穷举

能用动态规划求解的问题具有这样一种特征:在已经求解出 i - 1 规模的子问题的基础上,可以求解 i 规模的子问题

每次使用动态规划我都会被这个问题所困扰:后面新加入的元素到底会不会造成这次最优解无效?如果顺着这一思维陷阱下去,问题规模最后会变成指数级递增变成NPC问题

这也就是上面说的特征,我们只要能求出现有阶段的最优解,并且证明该解是有效的,这个DP方程完全成立,至于后来问题会不会干扰本次求解是后面需要考虑的问题

那么这时候会出现一个问题:局部最优解一定是全局最优解么?当然不是,我们在规模 i 下解出的最优解只能算局部最优解,他的意义是:如果问题到此为止,我们已经解出所有可能的最优解

2. DP问题为什么可以利用之前的最优解进行迭代

举一个整形线性规划的问题:输入为X=01011..,输出为F(X),求F的最大值。我的F()为黑盒,你无法判断X的某一位取0/1到底对最后结果有什么影响,这个问题没有办法迭代,只能指数级遍历

DP问题就不一样了,我们不需要知道问题的所有规模,我们只要知道之前规模情况下的解,在引入 i 之后可以利用之前的状态进行迭代计算

所以说,这个是由问题的本身特征决定的,该问题可以计算出部分规模的解

3. DP统一的参考性模型

尝试把DP问题归一化,提取出某些特征找出一个统一的解法

1)首先假设我们这个问题可以用DP求解。(其实满足以上讨论的条件,而分治和贪心又难以解决,可以确定是DP了)

2)划分问题。划分的时候一定要满足这样的特性:我们需要在完成 i - 1 的条件下完成 i ,并该次求解不能破坏之前已经求解的所有最优解,不要考虑对之后的影响,之后自有办法(一般从规模为 1 往后加到 N  为止)

3)决策过程。这部分要求我们写出DP的状态转移方程,也是最灵活,最烧脑的部分,可以分几步部分进行

设当前处理的单位为cur

a. 首先考察:加 1 个单位规模之后对问题全局的影响,也就说我们的全局最优解OPT是否与本次计算独立

不独立:OPT = DP[ i ]

独立:OPT = max {DP[ 0],DP[ 1 ]...DP[ i ]}

这个意思就是,我们所求的最优解是否包含了对cur的处理,cur本身是OPT的一部分,比如,对一串字符串的每个字符都进行处理,要计算OPT每个cur都要计算在列,这种问题是不独立的

不独立的情况也容易判断:某次的DP对OPT可能有贡献,也可能有它没它根本没有关系,比如,所有的挑选类问题,挑选若干个单位,OPT只是和部分单位相关,这种问题属于独立

b. 真正的难点从这里开始,我们加入了一个新的单位cur,需要找出与之关联的之前所有的状态集合

什么意思呢,我们的所有问题都是有约束的,前面已经说了在计算cur之前我们已经找到满足问题约束的所有最优解,那么加入了cur很显然会打破这一平衡

要让它重新满足约束条件,我们必须增加额外的转移消耗,这些转移消耗最终都计算在DP[ i ]内,而从哪些状态集合转换过来是我们需要考虑的

也就是说,加入了cur之后,和之前的哪些状态一起打破了问题的约束条件,我们需要找出所有之前的状态

这里多说一句,前面2)说过不用考虑当前对之后的影响,是因为之后会在这里考虑前面哪些状态对自己的影响

这里比较抽象,我多举一些例子:抢劫问题,连续两个房间不能被选中,那么cur状态就和 cur - 1 状态相关联;字符串回文划分,cur和之前所有可能的状态相关联;

二维DP[ i ][ j]与DP[ i - 1 ][ j ]以及DP[ i  ][ j - 1 ]相关联;挑选出某些单位满足条件,cur单位与之前所有状态相关联

c. 找到相关联单位的集合{pred(i)}之后,cur的加入与这些单元会产生矛盾,需要在外力F作用下进行转换,DP[ i ]满足这样的条件:

给定一个pred(i)

DP[ i ] = F (DP[pred(i)])

这个式子的意思是,cur在F的作用下使用pred(i)计算DP[ i ],把F具体点,分为两种:

DP[ i ] = DP[pred(i)] + trans

DP[ i ] = C

就是说cur可以在pred(i)的基础上加一个trans复杂度的处理可以满足约束条件

或者直接是一个常数C表示无法从pred(i)转换,cur和pred(i) 不共存,DP[ i ]仅由cur贡献

d. 对所有pred(i)进行以上操作,找到一个最优DP[ i ],这个DP[ i ]一定是目前cur的最优解,然后再将cur = cur + 1,迭代计算

e. 回到a,根据我们求解的所有DP,根据问题类型选择OPT模型

4. 总结

总结以上通用处理模型:

1)对问题的划分(i - 1规模,i 规模)

2)决策,考察问题特性选择OPT方程

找到与当前状态cur相关的集合{pred(i)}

对所有pred(i)求解DP[ i ] = pred(i) -> cur ,找出最优DP[ i ]  

 

这套框架是我之前遇到的所有DP问题的一个抽象,应该能应付大部分问题,后续有新的内容再进行补充

 

posted on 2018-04-29 23:54  Ruu  阅读(596)  评论(0编辑  收藏  举报