动态规划 (DP)12 Apr 2025 | 8 分钟阅读 动态规划 (DP) 是一种强大的方法,用于在计算机科学和数学中通过将复杂问题分解为更小的子问题来解决它们。与反复解决相同子问题的蛮力方法不同,DP 通过存储结果并重用它们来优化计算。本文将探讨动态规划的基础、概念、应用和实现策略。 理解动态规划动态规划 (DP) 是一种优化方法,通过将问题分解为重叠的子问题并存储其结果以避免重复计算来解决问题。当一个问题显示出最优子结构时(即,最优解可以从子问题的最优解构建而成),它就适用。 DP 可以通过以下两种方法实现:
它广泛应用于 Fibonacci 级数、最短路径、背包问题和字符串处理(例如,最长公共子序列)等问题。与蛮力方法相比,DP 极大地提高了效率。 动态规划的关键属性动态规划 (DP) 是一种问题解决方法,用于通过将问题分解为更小的子问题、仅解决每个子问题一次并存储结果以避免重复计算来优化递归解决方案。如果一个问题具有以下关键属性,则可以使用 DP 高效地解决: 1. 最优子结构 如果一个问题的最优解可以从其子问题的最优解构建而成,则称该问题具有最优子结构。这意味着递归地求解更小的子问题将导致全局最优解。 示例:最短路径问题 考虑在图中查找节点之间的最短路径。如果从节点 A 到节点 C 的最短路径经过节点 B,则: ShortestPath(𝐴 , 𝐶) = ShortestPath (𝐴, 𝐵) + ShortestPath(𝐵 , 𝐶) 这意味着从 A 到 C 的最短路径包含从 A 到 B 和从 B 到 C 的最优最短路径。Dijkstra 算法和 Floyd-Warshall 算法等算法都依赖于此属性。 示例:0/1 背包问题 在 0/1 背包问题 中,目标是最大化装入容量有限的背包中的物品的总价值。如果选择了一组最优物品,那么这些物品的任何子集也应该是对应于缩小容量的最优解。 0/1 背包问题的递归关系是: K(n, W) = max(K (n – 1 , W), Vn + K(n – 1 , W − Wn)) 其中: K(n,W) 是使用 𝑛 件物品和重量容量 𝑊 可以获得的最大价值。 𝑉𝑛 是第 𝑛 件物品的价值。 𝑊𝑛 是第 𝑛 件物品的重量。 如果一个问题缺乏最优子结构,DP 可能不是最佳方法。 2. 重叠子问题 如果同一个子问题被求解多次,则称问题显示出重叠子问题。DP 存储并重用这些子问题的结果,而不是重新计算它们,从而减少了总体计算时间。 示例:Fibonacci 级数 Fibonacci 级数的定义是: F(n) = F(n − 1) + F(n − 2) 天真的递归方法由于多次重新计算许多值,导致指数时间复杂度。 天真的递归方法(无 DP) 由于重复计算,此方法导致时间复杂度为 O(2n)。 使用记忆化(自顶向下 DP)的优化方法 通过将计算出的 Fibonacci 值存储在字典(记忆)中,我们避免了重复计算,将时间复杂度降低到 O(n)。 使用制表(自底向上 DP)的优化方法 此方法使用表进行迭代求解,避免了递归并提高了效率。 为什么这些属性很重要
动态规划方法的类型动态规划 (DP) 是一种优化技术,通过将问题分解为更小的重叠子问题、存储它们的答案并重用它们以避免重复计算来解决问题。有两种主要的实现 DP 的方法:
这两种方法都旨在提高效率,但它们在如何求解和存储子问题结果方面有所不同。 1. 自顶向下方法(记忆化) 自顶向下方法,也称为记忆化,采用递归策略。问题被分解为更小的子问题,并且每个子问题都通过递归进行求解。一旦子问题被求解,它的结果就会存储在记忆化表中(缓存)。当相同的子问题在递归过程中再次出现时,算法不会重新计算它,而是从缓存中检索先前计算的值,从而减少了计算时间。 记忆化在并非所有子问题都需要求解时尤其有用。这种选择性求解使其成为许多问题的高效方法,在这些问题中可以跳过不必要的计算。然而,由于它依赖于递归,记忆化可能会由于递归调用堆栈而导致过高的内存使用,尤其是在递归深度大的问题中。 记忆化的主要优点之一是它避免了重复计算,这大大提高了具有指数时间复杂度问题的性能,例如计算 Fibonacci 数、最长公共子序列和背包问题。然而,记忆化也可能具有函数调用开销,因为每次递归调用都会增加额外的内存使用和堆栈操作。如果递归深度过大,这可能会导致堆栈溢出错误,使得记忆化不适用于需要极深递归调用的问题。 2. 自底向上方法(制表) 自底向上方法,也称为制表,是一种消除递归的迭代方法。制表不是通过动态地将问题分解为更小的子问题来求解问题,而是首先识别最小的子问题并迭代地求解它们,逐级构建原始问题的解决方案。使用表(数组或矩阵)来存储子问题的结果,确保每个子问题仅被求解一次并在后续计算中正确使用。 制表对于需要求解所有子问题才能计算最终结果的问题特别有效。与仅求解必需子问题的记忆化不同,制表会求解每个可能的子问题,即使其中一些在最终计算中并不需要。这有时会导致效率低下,尤其是在仅少数子问题相关的情况下。 制表的一个关键优点是它避免了递归,从而防止了堆栈溢出等问题并减少了函数调用开销。由于制表是迭代地填充表,因此在实践中通常比记忆化更快,尤其是在函数调用成本很高的语言中。但是,它可能会消耗比必需更多的内存,因为它会求解并存储所有子问题的值,即使它们对于最终答案不是必需的。 动态规划的应用动态规划广泛应用于各个领域,包括:
挑战与最佳实践虽然动态规划是一种强大的方法,但高效地应用它可能很困难。以下是一些要牢记的最佳实践:
结论动态规划是一项重要的解决问题技术,它能显著提高解决复杂问题的效率。通过将问题分解为更小、可重用的子问题,DP 可以优化解决方案并减少计算时间。掌握 DP 需要练习和对如何识别可以从中受益的问题的理解。无论是在算法竞赛还是在现实世界的应用中,DP 仍然是程序员工具箱中的一项关键工具。 制表中的一个重要优化是空间约简。如果任何时候只需要少数先前计算过的值,则可以用几个变量替换该表,从而大大降低空间复杂度。这通常在 Fibonacci 级数计算等问题中可见,其中任何一步只需要两个先前的值。 记忆化与制表的比较 记忆化和制表都旨在通过存储和重用子问题结果来优化递归问题。然而,它们的主要过程不同:记忆化采用递归的自顶向下方法,仅求解必需的子问题;而制表采用迭代的自底向上方法,求解所有子问题。 记忆化对于明显是递归的问题更为直观,因为它密切遵循问题的定义。然而,由于函数调用开销和深层递归限制,它可能会遇到性能问题。另一方面,制表通常更快、更节省内存,因为它消除了递归,但它可能会求解不必要的子问题,从而导致潜在的效率低下。 在递归深度过大的情况下,记忆化可能会导致堆栈溢出,因此制表是首选。相反,当只需要计算一小部分子问题时,记忆化通常更有效,因为它避免了计算不必要的值。 何时使用记忆化与制表 记忆化和制表之间的选择取决于问题的性质和约束。如果问题明显是递归的,并且只需要计算少数子问题,那么记忆化是更好的方法。它允许以结构化的方式解决问题,在保持简单的递归方法的同时减少重复计算。 但是,如果为了确定最终答案需要计算所有子问题,那么制表是更好的选择。它避免了与递归相关的开销,使其对于输入量大的问题更有效。此外,如果问题由于深度递归有堆栈溢出的风险,那么制表通过使用迭代方法提供了更安全的选择。 对于具有严格内存限制的问题,可以使用空间优化的制表方法,通过仅存储必要的值而不是整个表来限制内存使用。这种技术在 Fibonacci 数计算和最短路径算法等问题中尤为有用。 |
可视化数据是分析海量数据的重要组成部分。Python 提供了许多用于此目的的库和函数,有助于创建简单而交互式的图形和图表。Matplotlib 是最常用、最普遍的此类库。
阅读 3 分钟
简介:只需几个简单的步骤,您就可以从命令行运行 Python 函数。首先,编写一个 Python 脚本(.py 文件),该脚本调用所需的函数。确保函数定义对齐且缩进正确。,然后启动命令行界面...
阅读 3 分钟
Python 中的 OverflowError 是一种特定的错误,当数值运算超出其处理的数据类型的边界时会发生。当尝试存储的值超出范围而发生溢出条件时,通常会出现此错误...
阅读 4 分钟
简介:Quine 是一种生成其代码副本但不接受任何输入的应用程序。在 C 语言中,我们已经讨论过 Quinine。在 Python 中编写最短的 Quine 只需要一行代码!Quine 是一个自我复制的程序,它...
阅读 3 分钟
引言 在编程中,优雅地处理错误是编写健壮且可维护代码的关键方面。Python 和许多其他编程语言一样,提供了一种强大的错误处理机制,称为异常。异常允许您以结构化且...
阅读 6 分钟
在接下来的教程中,我们将讨论用于管理 Python 项目的项目模板。但在我们开始之前,让我们简要了解一下什么是项目模板以及使用项目模板的优势。什么是项目模板?项目模板是预定义的...
阅读 4 分钟
迭代器是 Python 编程的基础,提供了一种遍历序列中元素的方法。虽然它们通常由 for 循环自动处理,但在需要手动控制或在循环内递增迭代器的情况下,也可能需要这样做。本文...
阅读 4 分钟
在 Python 中使用 OpenCV 对图像进行下采样相对直接。下采样是指降低图像的分辨率或尺寸。OpenCV 是 Python 中流行的图像处理库。您可以使用 `cv2.resize()` 函数执行下采样。这是一个简单的示例: import cv2 #...
阅读20分钟
简介 Python 的 os 模块提供了一种与操作系统交互的方式,其中包含各种处理文件和目录的方法。其中,os.stat() 方法作为检索文件或目录详细信息的强大工具而脱颖而出。这...
阅读 6 分钟
引言:在本教程中,我们将学习使用 Python 进行文本处理中的扩展缩写。文本预处理是 NLP 的主要步骤之一。清理我们的文本数据,以便将其转换为可分析的、可呈现的形式……
阅读 6 分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India