动态规划 | 豆包MarsCode AI刷题

100 阅读2分钟

动态规划的基本解题步骤:

  1. 定义状态:首先需要定义状态,通常是一个数组或矩阵,表示子问题的解。

    • 状态定义的核心是明确每个子问题的目标,比如最小/最大值、总和等。
  2. 状态转移方程:根据问题的结构,推导出当前状态与前一状态之间的关系。这个公式是动态规划的核心。

  3. 初始化:根据问题的边界条件,初始化状态数组。

  4. 计算顺序:按照一定的顺序(通常是从小问题到大问题),逐步计算出最终答案。

  5. 返回结果:根据状态数组(或表格)中最终的值来返回问题的解。

动态规划常见问题类型:

  1. 最值问题(如最大子序和、最小路径和等)

    • 求解问题的最优解,如最短路径、最大值、最小值等。
    • 典型问题:背包问题、股票买卖问题、最长公共子序列(LCS)等。
  2. 计数问题(如排列组合的计数)

    • 计算问题的解的个数,如从起点到终点的路径数、组合数等。
  3. 分段问题(如剪绳子问题、分割问题等)

    • 把问题分成若干段,分别求解后合并。

动态规划的经典例子:

1. 0/1 背包问题

问题描述:给定一个物品的重量和价值,求最大价值在不超过给定背包容量的条件下选择的物品集合。

def knapsack(weights, values, capacity):
    n = len(weights)
    dp = [0] * (capacity + 1)
    for i in range(n):
        for w in range(capacity, weights[i] - 1, -1):
            dp[w] = max(dp[w], dp[w - weights[i]] + values[i])
    return dp[capacity]

时间复杂度:O(n * W),其中n是物品数量,W是背包容量。

2. 最长公共子序列(LCS)

问题描述:给定两个字符串,找出它们的最长公共子序列的长度。

def longestCommonSubsequence(text1, text2):
    m, n = len(text1), len(text2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if text1[i - 1] == text2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    return dp[m][n]

时间复杂度:O(m * n),其中m和n是两个字符串的长度。

3. 爬楼梯问题

问题描述:每次可以爬1阶或2阶,求到达第n阶的不同方法数。

def climbStairs(n):
    if n == 1:
        return 1
    dp = [0] * (n + 1)
    dp[1], dp[2] = 1, 2
    for i in range(3, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]

时间复杂度:O(n),空间复杂度可以优化为O(1)。