动态规划dp学习笔记
一、 动态规划原理
转自
https://blog.csdn.net/ggdhs/article/details/90641251
https://blog.csdn.net/qq_20011607/article/details/82929611
1. 基本概念
动态规划通过拆分问题,将问题拆分成许多的子问题,定义问题状态和状态之间的关系(即状态转移方程或递推公式),使得问题能够以递推(或者说分治)的方式去解决。按顺序求解子问题,前一子问题的解,为后一子问题的求解提供了有用的信息,在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。 [2] 。
重点在于如何将问题拆分成子问题
1.1 动态规划和递归之间的关系
动态规划本质上就是递归(+记忆化搜索),动态规划通过将问题划分为若干个子问题,通过递推关系,一步一步求得最终问题的关系。但是子问题之间可能会有重复,因此如果单纯的使用递归方法来实现动态规划问题时间复杂度会比较高。但是,一般能用递归解决的问题都可以转化为用动态规划解决。
2. 动态规划适用情况
(1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。最优子结构应可以理解为大问题可以不断的分解为小问题,通过每个小问题的最优解可以得到相应大问题的最优解。
(2)无后效性:即某阶段状态(最优解)一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。就比如前面的裴波那契数列的递归算法,进行一次递归f(1)就被计算一次,因此裴波那契数列可以利用动态规划的方法进行求解。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势,在动态规划算法中使用数组来保存子问题的解,这样子问题多次求解的时候可以直接查表不用调用函数递归。)
二、动态规划的经典模型
转自
http://cppblog.com/menjitianya/archive/2015/10/23/212084.html
首先,下面几句话是显然的:
1.每个人都需要过桥;
2.最慢的两个人过桥后永远不会回来;(因为不会有人需要他们帮着过桥,也不会需要他们送手电筒,当然默认N>=4以保证最慢和最快不重合)
3.最慢的两个人不能在对面没有人而这边还有人的情况下两个人一起过桥;(否则就违反了第二条,必须有一个人回来送手电筒)
好,有了上面三句话,我们很容易得到两个推论。
1.要考虑把N个人送过去的最优时间,只需要先考虑怎么把最慢的两个人先送过去的最优时间。(类似于动态规划中的无后效性);
2.既然最慢的两个人不能独自过桥,那必须需要别人的帮助。那么需要谁的帮助呢?显然是最快的人的帮助。最快的几个人呢?讨论呗。(但是可以证明最多不超过两个人,因为假设有第三个人,任何一步他的帮助都没有什么作用)
这样,我们就已经知道为什么要选出这四个人了。现在来分析最优方案。
假设,t[1]~t[N] 是从小到大排好序的。设 A=t[1],B=t[2],C=t[N-1],D=t[N].
现在开始讨论:
1.如果C,D需要一个人的帮助过桥。那么最好的方案肯定是A把C送过去,A回来,A把D送过去,A回来。(A必须要回来,因为这边至少还有个B吧~)
T1=C+A+D+A
2.如果C,D需要两个人的帮助过桥。那么最好的方案肯定是A把B送过去,A回来,C和D过去,B回来。(B必须要回来,因为这边至少还有个A吧~)
T2=B+A+D+B
所以,只需要比较T1和T2的大小就可以确定选择哪一个方案。由公式,显然只需要比较A+C和2*B的大小。
而且,这两个方案的初始状态和终止状态都是一样的,而且选择之后的效果都是,左边的人少了两个。
那么问题就自然的转化为了N-2个人的过桥问题。就是递归的思想。
下面分析一下边界。
如果有三个人A,B,C(由小到大)。显然是用A送C,A回来,A送B。时间T=A+B+C。
如果有两个人。时间就是最大的时间。
一个人。时间就是这个人的时间。
分解子问题 -> 最后一步 -> 第 i 步 -> 第一步
从第一步到最后一步相当于dfs,要记录所有中间状态,无已知解;
从最后一步到第一步,有已知解。
博弈论问题可能无法转换为dp
具体过程:
完全背包
https://www.cnblogs.com/mfrank/p/10803417.html
多重背包