C++动态规划基础入门
🌱 第一阶段:打好基础
1. 理解什么是动态规划
动态规划的本质是把一个复杂的问题分解为多个子问题,保存子问题的解以避免重复计算。
通俗地说,就是“记住你已经算过的东西,别重复做傻事”。
2. 掌握几个基本概念
-
状态定义(状态表示):用变量表示某个子问题的情况,比如
dp[i]
表示前 i 个物品的最优解。 -
状态转移方程:用之前的状态推出当前的,比如:
dp[i] = dp[i-1] + ...
-
初始条件(边界值):比如
dp[0] = 0
-
最终答案的选择:可能是
dp[n]
,也可能是max(dp[i])
🧠 第二阶段:理解并手写简单例题
推荐几个经典入门题:
-
斐波那契数列(经典一维 DP)
-
递归(慢) ➜ 记忆化搜索 ➜ DP数组 ➜ 滚动数组优化
-
-
爬楼梯问题
-
dp[i] = dp[i-1] + dp[i-2]
-
-
0-1背包问题(二维 DP)
-
状态定义:
dp[i][j]
表示前 i 个物品在容量 j 下的最大价值 -
状态转移:
dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i])
-
-
最长上升子序列 LIS
-
dp[i] = max(dp[j] + 1)
ifnums[i] > nums[j]
-
这些题都很适合在 C++ 中实现,训练你的数组使用和逻辑思维。
🧰 第三阶段:刷题+总结套路
建议网站:
-
Letcode
-
NOI全国青少年信息学奥林匹克竞赛
-
牛客网
-
洛谷(适合算法竞赛风格)
分类刷题方向:
-
一维 DP:爬楼梯、打家劫舍
-
二维 DP:背包问题、棋盘路径
-
区间 DP:回文串、石子合并
-
状态压缩 DP:旅行商问题(TSP)
-
树形 DP:树上选点、树的直径
-
背包类型进一步细分:
-
0-1 背包
-
完全背包
-
多重背包
-
分组背包
-
💡 技巧与建议
-
先暴力,再优化: 不要一上来就写动态规划,先写递归或回溯,跑不动了再思考“我是不是可以记录中间结果”。
-
调试技巧: 输出
dp
数组的每一步,看哪里更新出错。 -
多看别人的题解,但不要死背: 尝试用自己的语言复述状态转移方程。
-
自己动手画状态转移图/表格: 尤其是二维的,很容易看清楚转移路径。
-
写一套 DP 模板: 比如你可以整理一个 0-1 背包的代码框架,套着用,省脑子。
🥚 第一阶段:基础 DP 入门(简单易懂,思路清晰)
1. 斐波那契数列
-
🧠 关键点:递归 + 记忆化 + 迭代 DP
-
💡 状态转移:
dp[i] = dp[i-1] + dp[i-2]
-
💻 LeetCode 原题:509. Fibonacci Number
2. 爬楼梯
-
🧠 类似斐波那契,适合入门
-
💡 每次可以爬 1 或 2 级台阶,问有几种方法爬到第 n 级
-
💻 LeetCode 原题:70. 爬楼梯
3. 打家劫舍 I
-
🧠 房子不能连续偷,典型的“一维状态 + 选与不选”
-
💡 状态转移:
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
-
💻 LeetCode 原题:198. House Robber
🐣 第二阶段:二维 DP + 背包入门
4. 不同路径
-
🧠 从左上角走到右下角,只能向右或下走,问有几种路径
-
💡 状态转移:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
-
💻 LeetCode 原题:62. Unique Paths
5. 0-1 背包问题(基础竞赛题)
-
🧠 有若干物品和一个背包,选择物品装进去使总价值最大,不能重复选
-
💡 状态定义:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
-
💻 洛谷经典入门题:P1048 采药
🐉 第三阶段:稍进阶一点,挑战一下
6. 最长上升子序列(LIS)
-
🧠 很常见的竞赛题型,DP难度提升一点
-
💡
dp[i] = max(dp[j] + 1)
ifnums[i] > nums[j]
💻 LeetCode 原题:300. Longest Increasing Subsequence
7. 完全背包问题
-
🧠 和 0-1 背包不同,每个物品可以选无限次
-
💡 状态转移会略不同:一维优化:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
-
💻 洛谷:P1616 疯狂的采药