动态规划
目录
动态规划
动态规划
1、定义:动态规划是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划是分治思想的延伸,通俗一点来说就是大事化小,小事化无。
2、核心思想:
动态规划的核心思想,就在于拆分成子问题,记住过往,减少重复计算,从而获得原问题的最优解。
3、特点:
最优子结构:
动态规划将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。(“分”与“合”体现在状态转移方程)其实有时候用动态规划也不一定就是最优解那种意思。比如斐波拉契数列,我们很难从中体会到最优解的意味。我感觉这句话是在告诉我们:出现最优解的问题的时候,要第一时间想到动态规划,不是最优解的问题时,也要想到动态规划分解子问题的思想!
重叠子问题:
动态规划会将每个求解过的子问题的解记录下来,这样当下一次碰到同样的子问题时,就可以直接使用之前记录的结果,而不是重复计算。(虽然动态规划使用这种方式来提高计算效率,但不能说这种做法就是动态规划的核心)所谓记录就是dp数组。
4、动态规划的写法:
(1)递归(自上向下),即从目标问题开始,将它分解成子问题的组合,直到分解到边界为止。
(2)递推(自下向上),即从边界开始,不断向上解决问题,直到解决目标问题。
解题步骤
- 定义状态:确定问题的状态表示,通常用一个或多个变量来描述问题在不同阶段的情况。例如,在背包问题中,可以用\(dp[i][j]\)表示考虑前i个物品,背包容量为j时的最大价值。
- 状态转移方程:找出如何从已知状态推导出未知状态,即建立状态之间的递推关系。对于背包问题,状态转移方程为\(dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i])\),其中\(w[i]\)和\(v[i]\)分别是第i个物品的重量和价值。
- 初始化:确定初始状态的值。例如,在背包问题中,\(dp[0][j]=0\)表示没有物品时,无论背包容量是多少,价值都为0;\(dp[i][0]=0\)表示背包容量为0时,无论有多少物品,价值也为0。
- 确定计算顺序:根据状态之间的依赖关系,确定是采用自底向上(如先计算子问题\(dp[1][1]\),\(dp[1][2]\)等,逐步向上计算到\(dp[n][W]\))还是自顶向下(通过递归和记忆化搜索,从\(dp[n][W]\)开始,根据需要计算子问题)的方式计算状态值。
- 构造最优解:在计算出最优值后,根据记录的状态信息,构造出问题的最优解。例如,在背包问题中,可以通过回溯dp数组,确定选择了哪些物品放入背包。
应用场景
- 背包问题:包括 0-1 背包问题、完全背包问题等,用于解决在有限容量的背包中选择物品,以达到最大价值的问题。
- 最长公共子序列:给定两个序列,找出它们的最长公共子序列。例如,对于序列\(X = \{1, 3, 4, 5, 6, 7, 7, 8\}\)和\(Y = \{3, 5, 7, 4, 8, 6, 7, 8, 2\}\),它们的最长公共子序列是\(\{3, 5, 7, 8\}\)。
- 矩阵链乘法:给定一系列矩阵,确定它们的乘法顺序,以最小化乘法运算的次数。
- 图像识别:在图像分割、目标检测等领域,动态规划可以用于寻找最优的图像区域划分或目标定位。
- 语音识别:用于语音信号的特征提取和识别,例如在隐马尔可夫模型中,通过动态规划算法来计算最优的状态序列。
代码示例(以斐波那契数列为例)
斐波那契数列的动态规划实现代码如下:
def fibonacci(n):if n <= 1:return n# 创建一个列表来存储斐波那契数列的结果dp = [0] * (n + 1)# 初始化基本情况dp[0] = 0dp[1] = 1# 动态规划计算斐波那契数列for i in range(2, n + 1):dp[i] = dp[i - 1] + dp[i - 2]return dp[n]# 测试函数
n = 10
print(f"斐波那契数列的第 {n} 项是: {fibonacci(n)}")
这段代码使用动态规划的方法计算斐波那契数列的第n项。首先处理了基本情况,然后使用一个列表dp来存储已经计算出的斐波那契数,通过循环迭代计算出最终结果。这种方法避免了递归方法中的重复计算,提高了计算效率。