跳到主要内容

一维动态规划

动态规划(Dynamic Programming,简称 DP)是一种用于解决复杂问题的算法设计技术。它通过将问题分解为更小的子问题,并存储这些子问题的解,从而避免重复计算,提高效率。一维动态规划是动态规划中最基础的形式,通常用于解决只需要一个一维数组来存储状态的问题。

什么是动态规划?

动态规划的核心思想是分治记忆化。它将一个大问题分解为多个重叠的子问题,通过解决这些子问题来构建原问题的解。为了避免重复计算,动态规划会将这些子问题的解存储起来,以便在需要时直接使用。

动态规划的适用条件

  1. 最优子结构:问题的最优解可以通过子问题的最优解来构造。
  2. 重叠子问题:在解决问题的过程中,相同的子问题会被多次计算。

一维动态规划的基本结构

一维动态规划通常使用一个一维数组来存储子问题的解。这个数组的每个元素代表一个子问题的解,通过逐步填充这个数组,最终得到原问题的解。

基本步骤

  1. 定义状态:确定问题的状态,并用一个一维数组来表示这些状态。
  2. 状态转移方程:找到状态之间的关系,即如何从一个状态转移到另一个状态。
  3. 初始化:确定初始状态的值。
  4. 计算顺序:按照一定的顺序计算状态数组中的每个元素。
  5. 返回结果:从状态数组中提取最终的解。

示例:斐波那契数列

斐波那契数列是一个经典的动态规划问题。斐波那契数列的定义如下:

  • F(0) = 0
  • F(1) = 1
  • F(n) = F(n-1) + F(n-2) (n ≥ 2)

代码实现

python
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1

dp = [0] * (n + 1)
dp[0] = 0
dp[1] = 1

for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]

return dp[n]

输入与输出

  • 输入:n = 5
  • 输出:5
备注

在这个例子中,我们使用一个一维数组 dp 来存储斐波那契数列的值。通过逐步计算 dp[i],我们最终得到了 F(5) 的值。

实际应用:爬楼梯问题

假设你正在爬楼梯,每次你可以爬 1 阶或 2 阶。问:有多少种不同的方法可以爬到第 n 阶?

分析

这个问题可以转化为斐波那契数列问题。设 dp[i] 表示爬到第 i 阶的方法数,那么:

  • dp[0] = 1 (表示在地面,只有一种方法)
  • dp[1] = 1 (只有一种方法爬到第 1 阶)
  • dp[i] = dp[i-1] + dp[i-2] (可以从第 i-1 阶爬 1 阶,或者从第 i-2 阶爬 2 阶)

代码实现

python
def climb_stairs(n):
if n == 0:
return 1
elif n == 1:
return 1

dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1

for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]

return dp[n]

输入与输出

  • 输入:n = 3
  • 输出:3
提示

在这个问题中,我们再次使用了一维动态规划的思想。通过存储子问题的解,我们避免了重复计算,大大提高了效率。

总结

一维动态规划是动态规划中最基础的形式,适用于许多只需要一个一维数组来存储状态的问题。通过定义状态、状态转移方程、初始化和计算顺序,我们可以有效地解决许多复杂的问题。

附加资源

练习

  1. 编写一个函数,计算第 n 个斐波那契数。
  2. 解决爬楼梯问题,假设每次可以爬 1 阶、2 阶或 3 阶。
  3. 尝试解决其他一维动态规划问题,如“最大子数组和”问题。

通过不断练习,你将更好地掌握一维动态规划的思想和应用。