返回

动态规划算法指南:揭开优化难题的面纱

前端

动态规划的本质

动态规划是一种通过将复杂问题分解成更小、更易于管理的子问题来解决的优化算法。它的三个关键步骤是:

  1. 定义子问题: 将原始问题分解成较小的子问题。
  2. 递归求解: 递归地解决这些子问题,从最简单的开始。
  3. 备忘录或表格: 存储子问题的解决方案,以避免重复计算。

动态规划的优势

  • 最优子结构: 子问题的最优解可以用来找到原始问题的最优解。
  • 重叠子问题: 相同的子问题可能会在递归过程中多次求解。
  • 效率: 通过避免重复计算,动态规划可以显著提高效率。

动态规划的应用

动态规划在解决广泛的问题中都有应用,包括:

  • 最长公共子序列
  • 背包问题
  • 矩阵链乘
  • 最短路径
  • 编辑距离

实战实例

最长公共子序列

给定两个字符串,求出它们的最长公共子序列。例如,对于 "ABCD" 和 "EDCB",最长公共子序列是 "BD"。

def lcs(s1, s2):
    # 创建一个表格来存储子问题的解决方案
    dp = [[0 for _ in range(len(s2) + 1)] for _ in range(len(s1) + 1)]
    
    # 填充表格
    for i in range(1, len(s1) + 1):
        for j in range(1, len(s2) + 1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    
    # 构造最长公共子序列
    lcs = ""
    i, j = len(s1), len(s2)
    while i > 0 and j > 0:
        if s1[i-1] == s2[j-1]:
            lcs = s1[i-1] + lcs
            i -= 1
            j -= 1
        else:
            if dp[i-1][j] > dp[i][j-1]:
                i -= 1
            else:
                j -= 1
    
    return lcs

背包问题

给定一组物品,每件物品都有重量和价值,以及一个背包容量。求出在不超过背包容量的情况下,装入背包的最大总价值。

def knapsack(items, capacity):
    # 创建一个表格来存储子问题的解决方案
    dp = [[0 for _ in range(capacity + 1)] for _ in range(len(items) + 1)]
    
    # 填充表格
    for i in range(1, len(items) + 1):
        for j in range(1, capacity + 1):
            if items[i-1][1] <= j:
                dp[i][j] = max(dp[i-1][j], dp[i-1][j - items[i-1][1]] + items[i-1][0])
            else:
                dp[i][j] = dp[i-1][j]
    
    # 追踪选择的物品
    selected = []
    i, j = len(items), capacity
    while i > 0 and j > 0:
        if dp[i][j] != dp[i-1][j]:
            selected.append(items[i-1])
            j -= items[i-1][1]
        i -= 1
    
    return dp[len(items)][capacity], selected

结论

动态规划是一种强大的优化算法,可以通过将复杂问题分解成更小的子问题来解决。通过理解其基本原理和实际应用,您可以掌握解决各种优化问题的强大工具。