【动态规划】基本概念

动态规划是解决问题的一种思想,而不是一个具体算法。此类问题的重点是:

  • 状态划分
  • 状态转移方程

常见的动态规划类型

  • 线性 DP:状态的排布是线性的,例如最长单调上升子序列
  • 区间 DP:在区间上 DP,是线性DP的扩展。f[i][j] 可以由 [i, j] 的子区间的最优解更新得到
  • 树状 DP:状态的排布是树状的。例如数塔
  • 背包 DP:背包体积有限,每个物品有价值,求能装下的最大价值
  • 数位 DP:区间里有多少数字包含某个性质
  • 状态压缩 DP:把状态用二进制表示

常见的动态规划优化

  • 滚动数组优化:节省空间
  • 矩阵乘法优化:
  • 斜率优化:
  • 四边形不等式优化:
  • 决策单调性优化:
  • 数据结构优化:优化时空

能用动态规划解决的问题类型

并不是所有问题都能用动态规划。要使用动态规划,需要满足三个条件:

  • 能划分阶段
  • 最优化原理:子问题的局部最优解会形成整个问题的全局最优解
  • 无后效性原则:某个阶段的状态一旦确定,后续过程的演变不再受此前的状态及决策影响。过去与未来无关。

动态规划解题方法

动态规划问题都可以通过划分阶段、进行决策得到一个递推方程。然后根据这个方程就可以写代码了。

写代码的时候要注意边界条件和递归结束条件。

  • 正推(递推):从初始状态开始,逐步决策,直到结束状态。例如阶乘递推式:F[i + 1] = F[i] * i
  • 倒推(记忆化搜索):从结束状态开始,倒着选择中间阶段的决策,到达开始状态。例如阶乘:dp[i] = dp[i - 1] * i

概念

示例:数塔问题:

			9
		4		7
	2		3		2
3		2		5		2

从数塔顶端的一个元素出发,每次可以向左下或右下移动,最终移动到最下层。求路径元素和的最大值。

阶段

把问题的全过程拆分成若干个相互联系的阶段,从而把一个问题变成多阶段决策问题。

对于上面的数塔,每一层就是一个阶段。

状态

每个阶段包含若干个状态。

对于上面的数塔,每一层就是一个阶段,每一层的每个元素就是一个状态。

决策

问题解决过程中,每个阶段做出的选择就是决策。

对于上面的数塔,每次都可以选择向左下或右下移动,但最终只能选一个方向移动,这个最终的选择的方向就是决策。

每个决策对应一个决策变量。实际问题中的决策变量往往是受限定的,这个限定范围就是决策允许集合。

策略、最优策略

所有阶段的一次排列构成问题的全过程。全过程中决策变量的有序总体就是策略,也就是实际问题的一个解。

对于上面的数塔,每一层选出一个数,最终到最下层,这个路径就是一个策略。其中路径上所有元素之和最大的那条就是最优策略。

状态转移方程

在相邻两个阶段之间转移时,可以用状态转移方程描述。对前一阶段的决策产生后一阶段的状态。

示例

示例一 数塔问题

			9
		4		7
	2		3		2
3		2		5		2

从数塔顶端的一个元素出发,每次可以向左下或右下移动,最终移动到最下层。求路径元素和的最大值。

dp[i][j] 表示第 i 层的第 j 个元素对应的最大和。

  • 阶段:每一层就是一个阶段
  • 状态:每层的每个元素就是这一层的一个状态
  • 决策:在每个位置,都需要判断左上方元素和右上方元素累计和的大小,并用大的作为自己的上一个元素
  • 策略:第 i 层有 i 个状态可以选择,共有 i ! i! i! 个策略。
  • 状态转移方程:d[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + arr[i][j]

示例二 最长单调上升子序列 TODO

问题:给定一个数组,求其最长单调增的子序列。例如对于 [1 2 6 3 5],其解为 [1 2 3 5]

dp[i][j] 表示目前已经分析了前 i 个元素,这个元素的最长单调上升子序列的最后一个元素是 j。对于上面例子,dp[0][1] = 1,dp[1][2] = 2,dp[2][5] = 3,dp[3][3] = 3,dp[3][5] = 3,dp[4][4] = 4。

  • 阶段:每个元素对应一个阶段
  • 状态:选或不选
  • 决策:
  • 策略:共 2 n 2^n 2n 个策略
  • 状态转移方程:dp[i][j] = max( f[i-1][j] | j <= a[i], )

示例三 最大和

从 n 个数中取出 k 个数,使其和最大。

dp[i][j] 表示当前已经判断到了第 i 个数(从 0 ~ i),并取出了 j 个。

  • 阶段:每个数就是一个阶段
  • 状态:选择或不选择
  • 决策:判断 dp[i-1][j] (不选当前元素)与 dp[i-1][j-1] + arr[i] (选择当前元素)的大小,若后者大则选择当前元素,否则不选
  • 策略:每个元素都可以选则或不选,共 2 i 2^i 2i 个策略
  • 状态转移方程:dp[i][j] = max( dp[i-1][j], dp[i-1][j-1] + arr[i] )

你可能感兴趣的:(算法与数据结构)