【第一章:人工智能基础】03.算法分析与设计-(3)动态规划

第一章 人工智能基础

第三部分:算法分析与设计

第三节:动态规划

内容:基本概念,常见问题(如最长公共子序列、背包问题)的解法与优化


一、基本概念

动态规划是一种将复杂问题分解为子问题、通过保存子问题的解避免重复计算的算法策略。适用于具有**重叠子问题(overlapping subproblems)最优子结构(optimal substructure)**的问题。

  • 子问题重叠:相同的子问题会反复出现。

  • 最优子结构:一个问题的最优解依赖于其子问题的最优解。

  • 状态转移方程:描述当前状态如何由前一个状态转移而来。

  • 记忆化搜索(自顶向下):递归 + 缓存。

  • 递推(自底向上):从最小子问题一步步迭代到大问题。


二、经典问题讲解
1. 最长公共子序列(LCS)

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

状态定义

  • dp[i][j] 表示 A 的前 i 个字符与 B 的前 j 个字符的 LCS 长度。

状态转移方程

if A[i-1] == B[j-1]:
    dp[i][j] = dp[i-1][j-1] + 1
else:
    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

代码示例

def lcs(A, B):
    m, n = len(A), len(B)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if A[i-1] == B[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]

2. 0-1 背包问题

问题描述:给定一组物品,每个物品有重量和价值,在总重量不超过限制的情况下,选出若干物品使总价值最大。

状态定义

  • dp[i][w] 表示前 i 件物品在容量为 w 的情况下的最大价值。

状态转移方程

if w >= weight[i-1]:
    dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i-1]] + value[i-1])
else:
    dp[i][w] = dp[i-1][w]

代码示例

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

三、优化技巧
  • 滚动数组:空间优化。二维 dp[i][w] 可以优化为一维 dp[w]

  • 状态压缩:利用状态的可合并性简化空间。

  • 路径恢复:通过记录路径重建实际方案(如 LCS 字符串、选中物品等)。


四、适用场景
  • 序列匹配(LCS、编辑距离)

  • 背包问题

  • 股票买卖问题

  • 数字拆分与计数(完全背包、分数问题)

  • 博弈论问题(石子游戏)


股票买卖问题(Best Time to Buy and Sell Stock)
问题描述

给定一个数组 prices,其中第 i 天的价格为 prices[i],可以进行一次买卖(先买后卖),求最大收益。

状态定义
  • min_price:到目前为止的最低价格。

  • max_profit:到目前为止的最大利润。

示例代码(买卖一次)
def max_profit(prices):
    min_price = float('inf')
    max_profit = 0
    for price in prices:
        min_price = min(min_price, price)
        max_profit = max(max_profit, price - min_price)
    return max_profit

股票买卖进阶(可进行 k 次交易)

状态定义:

  • dp[i][k][0]:第 i 天,最多进行 k 次交易,不持股的最大收益。

  • dp[i][k][1]:第 i 天,最多进行 k 次交易,持股的最大收益。

状态转移方程:

dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

该模型也可用于:

  • 多次买卖(k 次)

  • 冷却期

  • 手续费

  • 同时持有多股等情况(在 AI 中可用于强化学习环境建模)


博弈论问题:石子游戏(Stone Game)
问题描述

两人轮流从一堆石子中取走 1~3 个,取走最后一颗石子者胜。给定总石子数 n,问先手是否必胜?

思路

对每一个 n,我们都判断当前局面是否存在一个取法,使得对手处于“必败状态”。这是一种“胜负状态反推”的问题,适合用动态规划解决。

状态定义
  • dp[i] 表示面对 i 个石子时,先手是否能赢(True 表示必赢,False 表示必败)。

状态转移方程
dp[i] = not dp[i-1] or not dp[i-2] or not dp[i-3]

即,只要存在一种取法,使对方进入失败状态,先手就能赢。

示例代码
def can_win(n):
    dp = [False] * (n + 1)
    for i in range(1, n + 1):
        if i >= 1 and not dp[i-1]:
            dp[i] = True
        elif i >= 2 and not dp[i-2]:
            dp[i] = True
        elif i >= 3 and not dp[i-3]:
            dp[i] = True
    return dp[n]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值