Python学习算法系列(10):动态规划与贪心算法
1. 引言
动态规划(Dynamic Programming, DP)和贪心算法(Greedy Algorithm)是两种常见的算法设计思想,它们常用于解决优化问题。在很多情况下,动态规划和贪心算法能够有效地解决问题,但两者的适用场景和解决思路有所不同。
- 动态规划:适用于存在重叠子问题的情况,通过保存中间结果避免重复计算。
- 贪心算法:适用于局部最优解能够导向全局最优解的情况。
本篇博客将介绍:
- 动态规划的基本思想与应用
- 贪心算法的基本思想与应用
- 二者的比较与联系
2. 动态规划
2.1 动态规划的基本思想
动态规划(DP)是一种将问题分解为子问题,并通过保存子问题的结果来避免重复计算的算法设计思想。DP 主要解决的是 最优化问题,例如:最短路径问题、背包问题、最长公共子序列等。
动态规划的步骤:
- 定义状态:确定问题的状态表示。
- 状态转移方程:找到不同状态之间的关系。
- 边界条件:设定初始状态。
- 计算结果:通过状态转移方程迭代计算最终结果。
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):图论算法与搜索算法,敬请期待!🚀
💡 如果你喜欢这篇文章,欢迎点赞、收藏,并关注本系列!
1017

被折叠的 条评论
为什么被折叠?



