动态规划(DP)的核心概念
动态规划是一种通过将问题分解为子问题并存储子问题解来优化计算的算法策略。适用于具有重叠子问题 和最优子结构性质的问题。
- 重叠子问题:子问题在求解过程中被多次重复计算,例如斐波那契数列的递归实现。
- 最优子结构:问题的最优解包含子问题的最优解,例如最短路径问题。
DP问题的解决步骤
-
定义状态
明确问题的状态表示,通常用数组或矩阵存储子问题的解。例如,
dp[i]表示第i个斐波那契数。 -
状态转移方程
建立子问题之间的关系式。例如斐波那契数列的转移方程为:
dp\[i\] = dp\[i-1\] + dp\[i-2\]
-
初始化与边界条件
设置初始值以避免无限递归。例如斐波那契数列中:
dp\[0\] = 0, \\quad dp\[1\] = 1
-
计算顺序
确定填表顺序(自顶向下或自底向上)。例如斐波那契数列通常采用自底向上的迭代法。
-
空间优化(可选)
根据状态转移的依赖关系压缩存储空间。例如斐波那契数列只需两个变量代替整个数组。
经典DP问题示例
背包问题
问题描述 :给定物品重量weight和价值value,在容量为W的背包中求最大价值。
- 状态定义 :
dp[i][j]表示前i个物品在容量j时的最大价值。 - 状态转移 :
dp\[i\]\[j\] = \\max(dp\[i-1\]\[j\], \\quad dp\[i-1\]\[j - \\text{weight}\[i\]\] + \\text{value}\[i\])
最长公共子序列(LCS)
问题描述 :求两个字符串X和Y的最长公共子序列长度。
- 状态定义 :
dp[i][j]表示X[0..i-1]和Y[0..j-1]的LCS长度。 - 状态转移 :
dp\[i\]\[j\] = \\begin{cases} dp\[i-1\]\[j-1\] + 1 \& \\text{if } X\[i-1\] = Y\[j-1\], \\ \\max(dp\[i-1\]\[j\], dp\[i\]\[j-1\]) \& \\text{otherwise}. \\end{cases}
DP与分治的区别
- 分治:子问题独立无重叠(如归并排序),通常用递归实现。
- DP:子问题重叠,通过记忆化或填表避免重复计算。
代码示例(斐波那契数列)
python
def fibonacci(n):
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
通过理解核心思想并练习经典问题,可以逐步掌握动态规划的解题模式。