1. 动态规划(Dynamic Programming, DP)
动态规划是一种用于解决具有重叠子问题和最优子结构性质的问题的技术。它的核心思想是将问题分解为子问题,并将子问题的解存储起来(通常通过记忆化或表格化),以避免重复计算。
关键概念:
-
重叠子问题:问题可以分解为多个子问题,而这些子问题会被多次重复计算。
-
最优子结构:问题的最优解可以通过子问题的最优解来构造。
-
记忆化(Memoization):将子问题的解存储起来,以便后续直接使用,避免重复计算。
-
表格化(Tabulation):通过迭代的方式从最小的子问题开始,逐步构建更大的问题的解。
动态规划的两种实现方式:
-
自顶向下(Top-down):
-
使用递归方法解决问题。
-
通过记忆化存储子问题的解。
-
例如:斐波那契数列的递归实现。
-
-
自底向上(Bottom-up):
-
使用迭代方法解决问题。
-
从最小的子问题开始,逐步构建更大的问题的解。
-
例如:斐波那契数列的迭代实现。
-
动态规划的应用:
-
斐波那契数列
-
矩阵链乘法
-
最优二叉搜索树
-
0/1 背包问题
2. 分治法(Divide and Conquer, D&C)
分治法是一种将问题分解为多个独立的子问题,递归地解决这些子问题,然后将子问题的解合并以得到原问题的解的方法。
关键特点:
-
子问题之间相互独立,没有重叠。
-
通常使用递归实现。
-
适用于子问题不重叠的情况。
分治法的应用:
-
归并排序(Merge Sort)
-
快速排序(Quick Sort)
-
二分查找(Binary Search)
3. 动态规划与分治法的对比
特性 | 动态规划(DP) | 分治法(D&C) |
---|---|---|
子问题依赖 | 子问题之间相互依赖,存在重叠子问题。 | 子问题之间相互独立,没有重叠。 |
实现方式 | 通常使用迭代或递归+记忆化。 | 通常使用递归。 |
时间复杂度 | 更高效,避免重复计算。 | 较低效,可能重复计算子问题。 |
适用场景 | 适用于具有重叠子问题和最优子结构的问题。 | 适用于子问题独立的问题。 |
典型应用 | 斐波那契数列、矩阵链乘法、0/1 背包问题。 | 归并排序、快速排序、二分查找。 |
4. 斐波那契数列的动态规划实现
文件提供了一个斐波那契数列的动态规划实现示例:
-
递归定义:
fib(n) = fib(n-1) + fib(n-2)
。 -
问题:直接递归实现会导致大量重复计算,时间复杂度为指数级。
-
动态规划优化:
-
使用记忆化存储已计算的斐波那契数。
-
时间复杂度从指数级降低到线性级(O(n))。
-
代码示例:
python
复制
memo = {} # 记忆化表 def fib(n): if n in memo: return memo[n] # 如果已经计算过,直接返回 if n < 2: f = 1 # 基本情况 else: f = fib(n-1) + fib(n-2) # 递归计算 memo[n] = f # 存储结果 return f
5. 0/1 背包问题
0/1 背包问题是动态规划的经典应用之一。问题描述如下:
-
给定一组物品,每个物品有重量和价值。
-
背包有容量限制。
-
目标是在不超过背包容量的情况下,选择物品使得总价值最大。
示例:
-
硬币集合:
{1, 2, 3}
-
目标值:
5
-
问题:是否可以用最少数量的硬币凑出目标值?
6. 总结
-
动态规划通过存储子问题的解,避免了重复计算,适用于具有重叠子问题和最优子结构的问题。
-
分治法通过递归分解问题,适用于子问题独立的情况。
-
动态规划通常比分治法更高效,但实现复杂度较高。