「数据结构与算法」动态规划学习笔记:简介

「数据结构与算法」动态规划学习笔记:简介

动态规划的背景

动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

动态规划不是某一种具体的算法,而是一种算法思想:若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解

应用这种算法思想解决问题的可行性,对子问题与原问题的关系,以及子问题之间的关系这两方面有一些要求,它们分别对应了:

  • 最优子结构
  • 重复子问题

最优子结构

最优子结构规定的是子问题与原问题的关系

一个问题的最优解是由它的各个子问题的最优解决定的

将子问题的解进行组合,可以得到原问题的解是动态规划可行性的关键。在解题中一般用状态转移方程描述这种组合。例如原问题的解为 f(n),其中 f(n) 也叫状态。状态转移方程 f(n) = f(n - 1) + f(n - 2) 描述了一种原问题与子问题的组合关系 。

重复子问题

重复子问题规定的是子问题与子问题的关系

重复子问题不是保证解的正确性必须的,但是如果递归求解子问题时,没有出现重复子问题,则没有必要用动态规划,直接普通的递归就可以了。

例如,斐波那契问题的状态转移方程 f(n) = f(n - 1) + f(n - 2)。在求 f(5) 时,需要先求子问题 f(4) 和 f(3),得到结果后再组合成原问题 f(5) 的解。递归地求 f(4) 时,又要先求子问题 f(3) 和 f(2) ,这里的 f(3) 与求 f(5) 时的子问题重复了。

解决动态规划问题的核心

解决动态规划问题的核心:找出子问题及其子问题与原问题的关系

  • 找到了子问题以及子问题与原问题的关系,就可以递归地求解子问题了。(最优子结构)
  • 但重叠的子问题使得直接递归会有很多重复计算,于是就想到记忆化递归法:若能事先确定子问题的范围就可以建表存储子问题的答案。(重复子问题)

动态规划算法中关于最优子结构重复子问题的理解的关键点:

  1. 证明问题的方案中包含一种选择,选择之后留下一个或多个子问题
  2. 设计子问题的递归描述方式
  3. 证明对原问题的最优解包括了对所有子问题的最优解
  4. 证明子问题是重叠的(这一步不是动态规划正确性必需的,但是如果子问题无重叠,则效率与一般递归是相同的)

动态规划问题的思考过程

举个例子:

LeetCode 300
「数据结构与算法」动态规划学习笔记:简介_第1张图片

输入:“10,9,2,5,3,7,101,18”
输出:4
解释:最长的上升子序列是 2,3,7,101,它的长度是4。

对于这类问题,首先,需要思考如何将问题规模减小,一些典型的减小方式是动态规划分类的一句,例如线性、区间、树形等。
对于数组类问题,常见的减小问题规模的思路有两种:

  • 每次减少一半:如果每次减半,那么原问题变为:"10,9,2,5"和"3,7,101,18",两个问题的最优解分别为"2,5"和"3,7,101",但是找不到好的组合方式将两个子问题最优解组合为原问题最优解 "2,5,7,101"。
  • 每次减少一个:记 f(n) 为以第 n 个数结尾的最长子序列,每次减少一个,将原问题分为 f(n-1), f(n-2), ..., f(1),共 n - 1个子问题:
    n = 8:10,9,2,5,3,7,101,18 -->2,3,7,18
    n = 7:10,9,2,5,3,7,101 -->2,3,7,101
    n = 6:10,9,2,5,3,7 --2,3,7
    n = 5:10,9,2,5,3 -->2,3
    n = 4:10,9,2,5 --> 2,5
    n = 3:10,9,2 --> 2
    n = 2:10,9 --> 9
    n = 1:10 --> 10

已经有 7 个子问题的最优解之后,可以发现一种组合方式得到原问题的最优解:f(6) 的结果 [2,3,7] 且末尾数 7 小于 18,同时长度也是 f(1)~f(7) 中,结尾小于 18 的结果中最长的。f(7) 虽然长度为 4 比 f(6) 长,但结尾是不小于 18 的,无法组合成原问题的解。

以上组合方式可以写成一个式子,即状态转移方程:

f(n) = max f(i) + 1
其中 i < n 且 a[i] < a[n]

这种思考如何通过 f(1)...f(n-1)f(1)...f(n−1) 求出 f(n)f(n) 的过程实际就是在思考状态转移方程怎么写

总结: 解决动态规划问题最难的地方有两点:

  • 如何定义 f(n)
  • 如何通过 f(1), f(2), … f(n - 1) 推导出 f(n),即状态转移方程

动态规划对比分治和贪心

分治

解决分治问题的时候,思路就是想办法把问题的规模减小,有时候减小一个,有时候减小一半,然后将每个小问题的解以及当前的情况组合起来得出最终的结果。例如归并排序和快速排序,归并排序将要排序的数组平均地分成两半,快速排序将数组随机地分成两半。然后不断地对它们递归地进行处理。

这里存在有最优的子结构,即原数组的排序结果是在子数组排序的结果上组合出来的,但是不存在重复子问题,因为不断地对待排序的数组进行对半分的时候,两半边的数据并不重叠,分别解决左半边和右半边的两个子问题的时候,没有子问题重复出现,这是动态规划和分治的区别。

贪心

关于最优子结构:

  • 贪心:每一步的最优解一定包含上一步的最优解,上一步之前的最优解无需记录
  • 动态规划:全局最优解中一定包含某个局部最优解,但不一定包含上一步的局部最优解,因此需要记录之前的所有的局部最优解

关于子问题最优解组合成原问题最优解的组合方式:

  • 贪心:如果把所有的子问题看成一棵树的话,贪心从根出发,每次向下遍历最优子树即可,这里的最优是贪心意义上的最优。此时不需要知道一个节点的所有子树情况,于是构不成一棵完整的树
  • 动态规划:动态规划需要对每一个子树求最优解,直至下面的每一个叶子的值,最后得到一棵完整的树,在所有子树都得到最优解后,将他们组合成答案

结果正确性:

  • 贪心不能保证求得的最后解是最佳的,复杂度低
  • 动态规划本质是穷举法,可以保证结果是最佳的,复杂度高

参考

https://leetcode-cn.com/leetb...

你可能感兴趣的:(程序员)