动态规划(Dynamic Programming, DP) 就是 把一个大问题拆成小问题,记住小问题的答案,然后用这些答案来计算更大的问题,最终得到正确的结果。
🌟 一句话理解:
✅ “把问题拆小、存结果、一步步算大!”
🎯 生活中的例子:爬楼梯
想象一下,你家楼梯有 n
级,每次你可以 走 1 级 或 走 2 级,问你有几种方法可以爬到楼梯的顶端?
🌱 方法 1:普通递归(不聪明的方法)
你会想到:
- 如果你最后一步走的是 1 级,那么之前已经到达
n-1
级了,问题就变成 “从 0 级到 n-1 级有多少种方法?” - 如果你最后一步走的是 2 级,那么之前已经到达
n-2
级了,问题就变成 “从 0 级到 n-2 级有多少种方法?”
所以,爬到第 n
级的方法数就是:
f(n)=f(n−1)+f(n−2)f(n) = f(n-1) + f(n-2)f(n)=f(n−1)+f(n−2)
💡 代码
🌟 问题
- 这就像考试时,你老是问同一个问题 “老师,1+1=?”,老师要重复回答你很多遍,太浪费时间了!
- 递归会 重复计算相同的子问题,比如
climbStairs(4)
需要计算climbStairs(3)
和climbStairs(2)
,但climbStairs(3)
也会重复计算climbStairs(2)
,造成 大量重复计算,很慢!😵💫
🎯 方法 2:动态规划(聪明的方法)
如果我们 记住 之前算出来的结果,就可以避免重复计算!
这就像考试时,把答案写在笔记本上,下一次就可以直接查答案!
💡 代码
✅ 这样,我们每个 dp[i]
只算一次,不会重复计算,速度超级快!
🔥 再举一个例子:零钱兑换
💰 题目
你有很多 不同面值的硬币,想用它们拼出一定的金额 amount
,但希望 用最少的硬币数量,你该怎么做?
😵 方法 1:暴力递归
- 试着用 所有可能的硬币 来拼
amount
,然后找出最少的次数。 - 但这样计算会非常慢,因为 相同的金额会被计算很多次,就像考试一直问老师同样的问题!
🧠 方法 2:动态规划
- 把金额从 0 开始,一个个算出来,并存起来!
- 每个金额
dp[i]
= 取当前最小的硬币 + 剩余金额的最优解!
💡 代码
💡 你学会了吗?
方法 | 解释 | 适用场景 |
---|---|---|
普通递归 | 直接拆问题,但重复计算,慢! | 适用于小规模问题 |
动态规划 | 记住已经计算过的值,避免重复计算 | 适用于大规模问题,速度快! |
📌 记住动态规划的 3 个步骤
1️⃣ 定义 dp[i]
:问题拆分成小问题,比如 dp[i]
表示 到 i
级的方法数 或 凑 i
元最少硬币数。
2️⃣ 状态转移方程:找出 dp[i]
和 dp[i-1]
、dp[i-2]
的关系。
3️⃣ 初始化 dp
,然后用循环计算,最终输出 dp[n]
。
🎯 总结
✅ 动态规划 = 递归 + 记忆化存储 + 迭代优化!
🚀 它就像做笔记:记住计算结果,避免重复计算,让程序跑得更快! 🔥🔥🔥