一、动态规划的基本概念
动态规划(Dynamic Programming,简称 DP)是一种分治思想的优化版本,适用于解决具有重叠子问题和最优子结构特性的复杂问题。它的核心是通过存储中间计算结果(即“记忆化”)避免重复计算,从而提升算法效率。
动态规划的核心步骤可以概括为以下几点:
- 定义状态(State):问题的子结构。
- 确定状态转移方程(Transition Function):将问题逐步递归到基本子问题。
- 初始化(Initialization):为边界条件赋值。
- 计算顺序:自底向上或自顶向下(递归加记忆化)。
在使用 豆包MarsCode AI 刷题的过程中,我加深了对动态规划的理解,特别是在最大子序和和背包问题等经典题目上的应用。
二、动态规划应用示例:最大子序和问题
问题描述:
给定一个整数数组,找出具有最大和的连续子数组,并返回其最大和。
例如:
- 输入:
nums = [-2,1,-3,4,-1,2,1,-5,4] - 输出:
6(连续子数组[4,-1,2,1]的和最大)。
三、解决思路
-
明确状态和转移方程
定义dp[i]表示以第i个元素结尾的最大子数组和。- 如果
dp[i-1] > 0,则可以将nums[i]加入之前的子数组,即dp[i] = dp[i-1] + nums[i]。 - 如果
dp[i-1] <= 0,则nums[i]本身就是最佳选择,即dp[i] = nums[i]。
- 如果
-
优化空间复杂度
由于dp[i]仅依赖于dp[i-1],因此可以用一个变量代替数组,降低空间复杂度为 O(1)O(1)。 -
边界条件
- 数组为空时返回
0。 - 初始化
dp[0] = nums[0]。
- 数组为空时返回
四、代码实现
使用 MarsCode AI 提供的代码模板并调整优化后的实现如下:
def max_sub_array(nums: list[int]) -> int:
if not nums:
return 0
# 初始化当前和与最大和
current_sum = max_sum = nums[0]
for num in nums[1:]:
# 转移方程
current_sum = max(num, current_sum + num)
# 更新最大值
max_sum = max(max_sum, current_sum)
return max_sum
五、图解分析
以输入 nums = [-2,1,-3,4,-1,2,1,-5,4] 为例:
| i | nums[i] | current_sum | max_sum |
|---|---|---|---|
| 0 | -2 | -2 | -2 |
| 1 | 1 | 1 | 1 |
| 2 | -3 | -2 | 1 |
| 3 | 4 | 4 | 4 |
| 4 | -1 | 3 | 4 |
| 5 | 2 | 5 | 5 |
| 6 | 1 | 6 | 6 |
| 7 | -5 | 1 | 6 |
| 8 | 4 | 5 | 6 |
最终结果为 6。
六、学习心得
通过 MarsCode AI 的动态规划题库,我对以下几点有了更深刻的理解:
- 递归与迭代的关系
MarsCode AI 提供了递归和迭代两种实现方式的对比,帮助我理解了递归如何逐步优化为迭代,特别是在动态规划中避免栈溢出问题。 - 分解问题的技巧
学会了通过拆解大问题为子问题,并通过状态转移将复杂问题简单化。例如,MarsCode 的“分步指导”功能非常有助于逐步理解动态规划的每一步逻辑。 - 算法调试的重要性
动态规划在实际实现中容易出错,比如状态转移方程写错或初始化条件不准确。AI 的代码纠错功能不仅让我更快找到问题,还能自动生成更优解法。
七、对入门同学的建议
- 从经典问题入手
推荐从简单问题开始,例如斐波那契数列、爬楼梯问题,再逐步挑战较难的题目(如背包问题)。 - 善用错题本
动态规划的问题往往思路复杂,解法多样。将解题时的错题和不同解法记录下来,结合 AI 的题目分类推荐功能,集中突破弱点。 - 多看解析,多总结
MarsCode AI 的解析详细直观,非常适合入门者学习。此外,将问题的状态转移方程和边界条件归纳总结,能加深对问题的本质理解。 - 注重应用场景
动态规划并非只有算法比赛有用,它在工程问题中也有广泛应用。例如路径规划、资源分配问题,都可以用动态规划建模求解。
通过总结动态规划的学习过程,我对算法设计的系统性和条理性有了更清晰的认知,同时也发现 AI 在辅助算法学习中的独特价值。希望这些经验能为其他同学提供参考,帮助他们少走弯路。