1. 动态规划入门:从打家劫舍问题说起
第一次看到LeetCode 198题"打家劫舍"时,我正坐在星巴克啃算法书。这道题表面上是小偷选择问题,实则是动态规划(DP)的绝佳入门案例。动态规划这个听起来高大上的概念,其实核心就是"记住已经解决过的子问题答案"——就像我们做数学题时会把中间结果写在草稿纸上一样。
这道题的场景设定非常生活化:一排房屋,每个房屋有特定金额,相邻房屋被偷会触发报警。我们需要计算在不触发警报的情况下能偷到的最大金额。这种将抽象算法问题具象化的方式,正是LeetCode题目的魅力所在。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题分析与状态定义
2.1 暴力解法的问题
最直观的想法是枚举所有可能的偷窃组合:对于每个房子,选择偷或不偷,然后检查是否满足不相邻的条件。这种暴力解法的时间复杂度是O(2^n),当n=100时,计算量将达到惊人的1.26e+30次操作——比宇宙中的原子总数还要多。
提示:遇到需要做"选择"的问题,且当前选择会影响后续选择时,动态规划往往是最佳解决方案。
2.2 最优子结构识别
动态规划适用的关键条件是问题具有"最优子结构"性质。在打家劫舍问题中:
- 对于第i个房屋,最大收益取决于前i-1个房屋的最优解
- 这个最优解可以分解为更小规模的子问题
- 子问题之间存在重叠(多个路径可能到达同一个子问题)
2.3 状态转移方程推导
定义dp[i]为考虑前i个房屋时能获得的最大金额。对于第i个房屋,我们有两种选择:
- 偷第i个房屋:那么第i-1个房屋不能偷,此时金额为nums[i] + dp[i-2]
- 不偷第i个房屋:那么金额保持为dp[i-1]
因此状态转移方程为:
code复制dp[i] = max(dp[i-1], dp[i-2] + nums[i])
这个方程看似简单,却包含了动态规划的精髓:当前状态只依赖于有限的前置状态,通过组合这些子问题的解来构建更大问题的解。
3. 基础实现与复杂度分析
3.1 C语言实现细节
让我们仔细分析提供的C语言实现代码:
c复制int rob(int* nums, int numsSize) {
if(numsSize == 1) {
return nums[0];
}
