动态规划 (Dynamic Programming) 算法概念-Python示例

Python 实例详解

1. 斐波那契数列
# 传统递归方法 - 效率低下 O(2^n)
def fibonacci_recursive(n):
    if n <= 1:
        return n
    return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)

# 动态规划方法 - 时间复杂度 O(n),空间复杂度 O(n)
def fibonacci_dp(n):
    if n <= 1:
        return n
    
    # 创建状态数组
    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]

# 空间优化版本 - 空间复杂度 O(1)
def fibonacci_optimized(n):
    if n <= 1:
        return n
    
    prev2, prev1 = 0, 1
    
    for i in range(2, n + 1):
        current = prev1 + prev2
        prev2, prev1 = prev1, current
    
    return prev1
2. 硬币找零问题
# 找到组成金额所需的最少硬币数
def coin_change(coins, amount):
    # dp[i] 表示组成金额 i 所需的最少硬币数
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0  # 组成金额0需要0个硬币
    
    # 对于每个金额
    for i in range(1, amount + 1):
        # 尝试每种硬币
        for coin in coins:
            if coin <= i:
                # 状态转移方程
                dp[i] = min(dp[i], dp[i - coin] + 1)
    
    return dp[amount] if dp[amount] != float('inf') else -1

# 示例使用
print(coin_change([1, 3, 4], 6))  # 输出: 2 (3+3)
3. 编辑距离问题
# 计算两个字符串之间的最小编辑距离
def min_distance(word1, word2):
    m, n = len(word1), len(word2)
    
    # dp[i][j] 表示 word1[0...i-1] 转换为 word2[0...j-1] 的最小操作数
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    
    # 初始化边界条件
    for i in range(m + 1):
        dp[i][0] = i
    for j in range(n + 1):
        dp[0][j] = j
    
    # 状态转移
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if word1[i - 1] == word2[j - 1]:
                # 字符相同,无需操作
                dp[i][j] = dp[i - 1][j - 1]
            else:
                # 取三种操作的最小值
                dp[i][j] = min(
                    dp[i - 1][j] + 1,     # 删除
                    dp[i][j - 1] + 1,     # 插入
                    dp[i - 1][j - 1] + 1  # 替换
                )
    
    return dp[m][n]
4. 最长递增子序列
# 找到数组中最长递增子序列的长度
def length_of_lis(nums):
    if not nums:
        return 0
    
    # dp[i] 表示以 nums[i] 结尾的最长递增子序列长度
    dp = [1] * len(nums)
    
    for i in range(1, len(nums)):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)
    
    return max(dp)

# 优化版本 - 使用二分查找 O(n log n)
def length_of_lis_optimized(nums):
    import bisect
    
    tails = []
    
    for num in nums:
        # 二分查找插入位置
        pos = bisect.bisect_left(tails, num)
        
        if pos == len(tails):
            tails.append(num)
        else:
            tails[pos] = num
    
    return len(tails)
5. 背包问题
# 0-1背包问题:在限定重量下获取最大价值
def knapsack(weights, values, capacity):
    n = len(weights)
    
    # dp[i][w] 表示前i个物品在容量为w时的最大价值
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    
    # 状态转移方程
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            # 不选择第i个物品
            dp[i][w] = dp[i - 1][w]
            
            # 如果能选择第i个物品,比较选择和不选择的价值
            if w >= weights[i - 1]:
                dp[i][w] = max(
                    dp[i][w],
                    dp[i - 1][w - weights[i - 1]] + values[i - 1]
                )
    
    return dp[n][capacity]

# 空间优化版本
def knapsack_optimized(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]
6. 最大子数组和
# 求连续子数组的最大和
def max_subarray(nums):
    if not nums:
        return 0
    
    # dp[i] 表示以第i个元素结尾的最大子数组和
    dp = [0] * len(nums)
    dp[0] = nums[0]
    
    max_sum = dp[0]
    
    # 状态转移方程:dp[i] = max(nums[i], dp[i-1] + nums[i])
    for i in range(1, len(nums)):
        dp[i] = max(nums[i], dp[i - 1] + nums[i])
        max_sum = max(max_sum, dp[i])
    
    return max_sum

# 空间优化版本
def max_subarray_optimized(nums):
    if not nums:
        return 0
    
    max_sum = nums[0]
    current_sum = nums[0]
    
    for i in range(1, len(nums)):
        current_sum = max(nums[i], current_sum + nums[i])
        max_sum = max(max_sum, current_sum)
    
    return max_sum

实际应用场景

1. 计算机科学领域
  • 算法优化:在递归算法中避免重复计算
  • 字符串处理:文本编辑器的差异比较、拼写检查
  • 图形处理:图像压缩、模式识别
  • 编译器设计:语法分析、代码优化
2. 业务应用
  • 资源分配:项目管理中的任务调度
  • 金融领域:投资组合优化、风险评估
  • 电商推荐:个性化推荐系统
  • 物流运输:路径规划、库存管理
3. 生物信息学
  • DNA序列比对:基因序列相似性分析
  • 蛋白质结构预测:生物分子结构分析
4. 游戏开发
  • AI决策:游戏中的路径寻找
  • 关卡设计:难度平衡计算
5. 网络通信
  • 路由算法:网络数据包的最优路径选择
  • 数据压缩:文件压缩算法优化

动态规划解题模板

def dynamic_programming_solution(problem):
    # 1. 定义状态
    # dp[i] 或 dp[i][j] 表示什么含义
    
    # 2. 初始化边界条件
    # dp[0] = ..., dp[1] = ...
    
    # 3. 状态转移方程
    # for i in range(...):
    #     dp[i] = some_function(dp[i-1], dp[i-2], ...)
    
    # 4. 返回结果
    # return dp[n] 或其他形式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香蕉可乐荷包蛋

努力写有用的code

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值