【算法基础】6.五大算法之动态规划
参考资料
动态规划及常见例子https://blog.csdn.net/m0_37741420/article/details/107755314
关于最长公共子序列的填表法的原理讲解,详见《趣学算法》的4.3节的解算,严谨又生动
直观理解
和分治法一样将问题的求解分为若干步骤并按顺序解决,和分治法不同的是动态规划过程中前一步的结果为后一步提供了有用信息,最后一步的解是最终的解。
例子先行
走金字塔问题也是常见的动态规划问题,该问题的形式是:有一个数字金字塔,例如:
查找从最高点到底部任意处结束的路径(每一步可以从当前点走到左下方的点也可以到达右下方的点,即左右2个子节点),使路径经过数字的和最大。
在解该问题时,需要将数字金字塔稍微变形一下,变成下面形式
1 int[ ][ ] arr = { 2 {13}, 3 {11,8}, 4 {12,7,26}, 5 {6,14,15,8}, 6 {12,7,13,24,11} 7 };
解题思路:
(1)相加和最大;(2)自底向顶相加,下一层和上一层相加,和存储在较上一层,直到加到第1层;
解题步骤:
(1)阶段,从倒数第二层开始迭代,因为若从倒数第一层开始,无法和上层联动、也没有下层和它联动;
(2)状态,每层的状态表示为a(i,j)
(3)状态转移方程,a(i,j)=max(a(i+1,j),a(i+1,j+1))
(4) 确定边界条件,迭代到第1层为止
代码:
1 public class Pyramid { 2 public static void main(String[] args) { 3 int[ ][ ] arr = { 4 {13}, 5 {11,8}, 6 {12,7,26}, 7 {6,14,15,8}, 8 {12,7,13,24,11} 9 }; 10 11 int result = getMaxNum(arr); 12 System.out.println("用迭代法求得最大数字为:"+result); 13 } 14 15 private static int getMaxNum(int[][] arr){ 16 for(int i = arr.length-2; i>=0; i--){ 17 for(int j = 0; j <= i; j++){ 18 arr[ i ][ j ] += Math.max(arr[ i+1 ][ j ], arr[ i+1 ][ j+1 ]); 19 } 20 } 21 return arr[0][0]; 22 } 23 }
总结提炼
1思想
动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推的方式去解决。
2使用场景
能用动态规划解决的问题具有的特征:
(1)最优化原理
如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
这个特征较容易理解,常见就是在某个问题汇总存在最值.
(2)无后效性
即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
这个特征可以理解为:可以存储不同阶段的变量值。这样的话,在后续阶段中,就不用重复求一些之前阶段的值,即不必回溯。
(3)有重叠子问题
即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。
该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势。
该特征是说,解问题的不同阶段中,子问题是有重叠、依赖关系的。如果子问题不存在重叠、依赖关系,使用分治往往更好。
3使用步骤
在动态规划的步骤上,一种比较容易的步骤是:
(1)划分阶段
按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
动态规划不像分治法那样可以将问题划分为完全独立的子问题,动态规划中前后部分是有依赖关系的。
因为在最后一步中,解空间很清晰,所以在动态规划中一般采用“自底向上”的倒序方式来划分问题阶段,这也暗示了天然适合递归的方式来解决动态规划问题。
(2)确定状态
量化当前状态,以便将这些数据带入到下一阶段的求解中。
将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
(3)确定决策并写出状态转移方程
因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。
但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。
(4)寻找边界条件
给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。
拓展方向