Python学习算法系列(10):动态规划与贪心算法

部署运行你感兴趣的模型镜像

Python学习算法系列(10):动态规划与贪心算法

1. 引言

动态规划(Dynamic Programming, DP)和贪心算法(Greedy Algorithm)是两种常见的算法设计思想,它们常用于解决优化问题。在很多情况下,动态规划和贪心算法能够有效地解决问题,但两者的适用场景和解决思路有所不同。

  • 动态规划:适用于存在重叠子问题的情况,通过保存中间结果避免重复计算。
  • 贪心算法:适用于局部最优解能够导向全局最优解的情况。

本篇博客将介绍:

  • 动态规划的基本思想与应用
  • 贪心算法的基本思想与应用
  • 二者的比较与联系

2. 动态规划

2.1 动态规划的基本思想

动态规划(DP)是一种将问题分解为子问题,并通过保存子问题的结果来避免重复计算的算法设计思想。DP 主要解决的是 最优化问题,例如:最短路径问题、背包问题、最长公共子序列等。

动态规划的步骤:

  1. 定义状态:确定问题的状态表示。
  2. 状态转移方程:找到不同状态之间的关系。
  3. 边界条件:设定初始状态。
  4. 计算结果:通过状态转移方程迭代计算最终结果。

2.2 示例:0-1 背包问题

问题描述:给定一组物品,每个物品有重量和价值,背包能够承受的最大重量是 W,求如何选择物品放入背包,使得总价值最大。

def knapsack(weights, values, W):
    n = len(weights)
    dp = [[0] * (W + 1) for _ in range(n + 1)]  # dp[i][w] 表示前 i 个物品在容量 w 下的最大价值
    
    for i in range(1, n + 1):
        for w in range(1, W + 1):
            if weights[i - 1] <= w:
                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][W]

# 测试 0-1 背包问题
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
W = 5
print(knapsack(weights, values, W))  # 输出 7
解释
  • dp[i][w] 表示前 i 个物品在容量 w 下的最大价值。
  • 对每个物品,选择放入背包还是不放入背包,取两者的最大值。

2.3 动态规划的优化

动态规划常常需要优化空间复杂度。通过 滚动数组一维数组替代二维数组,可以大大减少空间的使用。

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

# 测试优化后的 0-1 背包问题
print(knapsack_optimized(weights, values, W))  # 输出 7

3. 贪心算法

3.1 贪心算法的基本思想

贪心算法是一种在每一步选择中都做出局部最优解的算法。贪心算法并不总能得到全局最优解,但在某些特定问题中,局部最优解能够导向全局最优解。贪心算法的核心思想是:局部最优解能带来全局最优解

3.2 示例:活动选择问题

问题描述:有 n 个活动,每个活动都有开始时间和结束时间,要求选择尽可能多的不重叠活动。

def activity_selection(start, end):
    n = len(start)
    activities = list(zip(start, end))
    activities.sort(key=lambda x: x[1])  # 按结束时间排序
    
    selected = []
    last_end_time = -1
    
    for activity in activities:
        if activity[0] >= last_end_time:  # 如果活动的开始时间不早于上一个选择的活动结束时间
            selected.append(activity)
            last_end_time = activity[1]
    
    return selected

# 测试活动选择问题
start = [1, 3, 0, 5, 8, 5]
end = [2, 4, 6, 7, 9, 9]
selected_activities = activity_selection(start, end)
print(selected_activities)  # 输出 [(1, 2), (3, 4), (5, 6), (8, 9)]
解释
  • 按活动结束时间排序,选择每个活动时只考虑当前活动的开始时间是否早于上一个已选择活动的结束时间。

3.3 贪心算法的特点

  • 局部最优选择:每一步选择当前最优解。
  • 贪心选择性质:通过局部最优解推导全局最优解。
  • 不一定能得到最优解:并非所有问题都能应用贪心算法,贪心算法需要满足特定条件。

4. 动态规划 vs 贪心算法

算法适用问题计算方式优点缺点
动态规划重叠子问题的最优化问题子问题递推计算适用范围广泛计算量大,空间复杂度高
贪心算法局部最优解可以导出全局最优解每步选择最优解计算速度快,简单易实现并不总是最优解

📌 示例对比

  • 0-1 背包问题适合使用动态规划,而活动选择问题适合使用贪心算法。

5. 总结

动态规划

  • 适用于具有重叠子问题的最优化问题。
  • 通过存储子问题的结果避免重复计算。
  • 解决问题时需要明确状态转移方程。

贪心算法

  • 适用于局部最优解能够导向全局最优解的问题。
  • 简单快速,但并不总是能得到最优解。
  • 解决问题时需要进行贪心选择。

📢 下一篇 Python学习算法系列(11):图论算法与搜索算法,敬请期待!🚀

💡 如果你喜欢这篇文章,欢迎点赞、收藏,并关注本系列!

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值