Skip to content

动态规划

动态规划是一种将复杂问题分解为子问题求解的算法思想。

背包问题

0-1 背包

给定一组物品,每个物品有重量和价值,在背包容量限制下选择物品使得总价值最大。

实现

python
def knapsack_01(weights, values, capacity):
    n = len(weights)
    # dp[i][j] 表示前 i 个物品,容量 j 时的最大价值
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for j in range(1, capacity + 1):
            if weights[i - 1] <= j:
                # 选择或不选择第 i 个物品
                dp[i][j] = max(
                    dp[i - 1][j],
                    dp[i - 1][j - weights[i - 1]] + values[i - 1]
                )
            else:
                dp[i][j] = dp[i - 1][j]
    
    return dp[n][capacity]

# 示例
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 8
max_value = knapsack_01(weights, values, capacity)
print(f"0-1 背包最大价值: {max_value}")  # 10

完全背包

每个物品可以选择多次。

实现

python
def knapsack_unbounded(weights, values, capacity):
    n = len(weights)
    dp = [0] * (capacity + 1)
    
    for i in range(1, capacity + 1):
        for j in range(n):
            if weights[j] <= i:
                dp[i] = max(dp[i], dp[i - weights[j]] + values[j])
    
    return dp[capacity]

# 示例
max_value = knapsack_unbounded(weights, values, capacity)
print(f"完全背包最大价值: {max_value}")  # 12

序列问题

最长递增子序列

python
def length_of_lis(nums):
    if not nums:
        return 0
    
    n = len(nums)
    dp = [1] * n
    
    for i in range(1, n):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)
    
    return max(dp)

# 示例
nums = [10, 9, 2, 5, 3, 7, 101, 18]
length = length_of_lis(nums)
print(f"最长递增子序列长度: {length}")  # 4

最长公共子序列

python
def lcs_length(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    for i in range(1, m + 1):
        for j in range(1, n + 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])
    
    return dp[m][n]

# 示例
s1 = "abcde"
s2 = "ace"
length = lcs_length(s1, s2)
print(f"最长公共子序列长度: {length}")  # 3

状态压缩

旅行商问题

python
def tsp(graph):
    n = len(graph)
    # dp[mask][u] 表示经过 mask 节点,最后到达 u 的最短路径
    dp = [[float('inf')] * n for _ in range(1 << n)]
    dp[1][0] = 0  # 从节点 0 出发
    
    for mask in range(1, 1 << n):
        for u in range(n):
            if not (mask & (1 << u)):
                continue
            
            for v in range(n):
                if mask & (1 << v):
                    continue
                
                new_mask = mask | (1 << v)
                dp[new_mask][v] = min(dp[new_mask][v], dp[mask][u] + graph[u][v])
    
    return dp[(1 << n) - 1][0]

# 示例图(邻接矩阵)
graph = [
    [0, 10, 15, 20],
    [10, 0, 35, 25],
    [15, 35, 0, 30],
    [20, 25, 30, 0]
]

min_distance = tsp(graph)
print(f"旅行商最短路径: {min_distance}")  # 80