第十七次博客打卡
今天学习的内容是动态规划算法。
动态规划算法(Dynamic Programming,简称 DP)是一种通过将复杂问题分解为更小的子问题来求解的算法思想。它主要用于解决具有重叠子问题和最优子结构特性的问题。
一、动态规划的基本概念
1. 最优子结构
一个复杂问题的最优解可以由其子问题的最优解组合而成。换句话说,问题的最优解包含其子问题的最优解。例如,在背包问题中,如果一个物品被选择放入背包,那么剩余容量下的最优解就是子问题的最优解。
2. 重叠子问题
在求解过程中,相同的子问题会被多次计算。动态规划通过存储这些子问题的解(通常使用一个表格),避免重复计算,从而提高效率。例如,在计算斐波那契数列时,`F(n) = F(n-1) + F(n-2)`,如果不使用动态规划,会重复计算很多子问题。
二、动态规划的解题步骤
1. 定义状态
状态是动态规划的核心,它表示问题的某个阶段的解。状态的定义需要满足两个条件:能够唯一表示问题的子问题,并且能够通过状态之间的关系推导出最终解。
2. 状态转移方程
状态转移方程描述了状态之间的关系,即如何从已知的状态推导出新的状态。这是动态规划的关键部分。
3. 初始化
确定动态规划的初始化。
经典问题
1.最长递增子序列(LIS)
• 给定一个序列,求其最长递增子序列的长度。例如,对于序列`[10, 9, 2, 5, 3, 7, 101, 18]`,最长递增子序列是`[2, 3, 7, 18]`,长度为 4。
• 状态定义:`dp[i]`表示以第`i`个元素结尾的最长递增子序列的长度。
• 状态转移方程:
\[
dp[i]=\max{0\le j<i\text{且}a_j<a_i}(dp[j]+1)
\]
其中,`a`是输入序列。
• 初始化:`dp[i] = 1`(每个元素自身可以构成长度为 1 的递增子序列)。
• 计算顺序:从`i = 0`到`n - 1`。
动态规划的优化技巧
1. 空间优化
• 在许多情况下,可以将二维动态规划表优化为一维数组。例如,在背包问题中,如果只关心最终结果,可以将`dp[i][j]`优化为`dp[j]`,通过从后向前更新`dp[j]`来避免覆盖问题。
2. 二分查找优化
• 在某些动态规划问题中,可以结合二分查找来优化状态转移。例如,在最长递增子序列问题中,可以使用二分查找来快速找到合适的插入位置,从而将时间复杂度从\(O(n^2)\)优化到\(O(n\log n)\)。
3. 滚动数组
• 当状态转移只依赖于前几行时,可以使用滚动数组来减少空间复杂度。例如,在二维动态规划中,如果状态转移只依赖于前一行,可以只使用两个一维数组交替更新。
动态规划是一种强大的算法思想,通过合理定义状态和状态转移方程,可以高效地解决许多复杂问题。