565 字
3 分钟
动态规划入门:从零开始理解 DP

什么是动态规划?#

动态规划(Dynamic Programming,简称 DP)是一种通过把原问题分解为相对简单的子问题来求解复杂问题的方法。

核心思想#

  1. 最优子结构:问题的最优解包含子问题的最优解
  2. 重叠子问题:不同的子问题有相同的子子问题
  3. 状态转移:从已知状态推导未知状态

经典例题:爬楼梯#

你需要爬 nn 阶楼梯,每次可以爬 1 阶或 2 阶。问有多少种不同的方法?

分析#

f(n)f(n) 表示爬到第 nn 阶的方法数:

f(n)={1n=12n=2f(n1)+f(n2)n>2f(n) = \begin{cases} 1 & n = 1 \\ 2 & n = 2 \\ f(n-1) + f(n-2) & n > 2 \end{cases}

C++ 实现#

#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
// dp[i] 表示爬到第 i 阶的方法数
int dp[n + 1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
cout << dp[n] << endl;
return 0;
}

经典例题:01 背包问题#

nn 个物品,第 ii 个物品重量为 wiw_i,价值为 viv_i。背包容量为 WW。求在不超过背包容量的前提下,能获得的最大价值。

状态定义#

dp[i][j]dp[i][j] 表示考虑前 ii 个物品、背包容量为 jj 时的最大价值。

状态转移方程#

dp[i][j]=max(dp[i1][j],  dp[i1][jwi]+vi)dp[i][j] = \max(dp[i-1][j], \; dp[i-1][j-w_i] + v_i)

C++ 实现#

#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1005;
int dp[MAXN];
int w[MAXN], v[MAXN];
int main() {
int n, W;
cin >> n >> W;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
}
// 一维优化
for (int i = 1; i <= n; i++) {
for (int j = W; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
cout << dp[W] << endl;
return 0;
}
NOTE

注意一维优化时,内层循环需要倒序遍历,以保证每个物品只被选一次。

练习题#

  1. 洛谷 P1048 采药 - 01 背包模板
  2. 洛谷 P1616 疯狂的采药 - 完全背包
  3. 洛谷 P1049 装箱问题 - 01 背包变形

总结#

问题类型状态定义转移方程时间复杂度
爬楼梯f(n)f(n):方法数f(n)=f(n1)+f(n2)f(n) = f(n-1) + f(n-2)O(n)O(n)
01 背包dp[i][j]dp[i][j]:前 ii 件、容量 jj 的最大价值dp[i][j]=max(dp[i1][j],dp[i1][jwi]+vi)dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w_i]+v_i)O(nW)O(nW)
TIP

DP 学习建议:先理解状态定义,再推导转移方程,最后考虑优化。

动态规划入门:从零开始理解 DP
https://blog.singlelyra.top/posts/teaching-dp-intro/
作者
ByteCraft
发布于
2026-03-18
许可协议
CC BY-NC-SA 4.0