动态规划是信奥赛的第一大坎,很多孩子卡在这里,怎么学都学不会。
其实,动态规划就那么几个经典模型。把这几个模型吃透,DP就通了。
今天,我总结5个最常见的DP模型,附代码模板和例题,让孩子快速掌握DP精髓。
问题描述:有N件物品和一个容量为V的背包,每件物品有重量w[i]和价值v[i],求能装入的最大价值。
代码模板:
// 01背包 - 一维优化
for (int i = 1; i <= n; i++)
for (int j = V; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
经典例题:洛谷P1048 [NOIP2005] 采药
学习要点:理解“选或不选”两种状态,掌握一维优化的原理。
问题描述:给定一个序列,求最长的严格递增子序列的长度。
代码模板:
// O(n²)写法
for (int i = 1; i <= n; i++) {
dp[i] = 1;
for (int j = 1; j < i; j++)
if (a[j] < a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
// 答案是max(dp[1..n])
经典例题:洛谷P1020 [NOIP1999] 导弹拦截
学习要点:掌握O(n²)和O(nlogn)两种写法。
问题描述:给定两个序列,求它们最长的公共子序列的长度。
代码模板:
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (a[i] == b[j])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
经典例题:洛谷P1439 【模板】最长公共子序列
学习要点:理解二维DP的状态转移。
问题描述:在一个区间上进行动态规划,状态通常用dp[l][r]表示区间[l,r]的最优解。
代码模板:
for (int len = 2; len <= n; len++) // 区间长度
for (int l = 1; l + len - 1 <= n; l++) { // 区间左端点
int r = l + len - 1; // 区间右端点
for (int k = l; k < r; k++) // 分割点
dp[l][r] = max(dp[l][r], dp[l][k] + dp[k+1][r] + w[l][r]);
}
经典例题:洛谷P1880 [NOI1995] 石子合并
学习要点:掌握区间DP的枚举顺序。
问题描述:在树结构上进行动态规划,通常用DFS实现。
代码模板:
void dfs(int u, int fa) {
for (int v : g[u]) {
if (v == fa) continue;
dfs(v, u);
// 状态转移,比如选或不选
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
}
经典例题:洛谷P1352 没有上司的舞会
学习要点:理解DFS和DP的结合。
学员小博的经历:“我学DP时,前两周完全懵的。后来薛老师让我把这5个模型各做10道题,每道题都画表格、写思路。做完50题后,突然就开窍了。现在看到DP题,先想‘这是哪个模型’。”
薛老师点评:DP不是靠理解,是靠“刷模型”。把这5个模型吃透,DP就通了。
| 模型 | 建议题量 | 关键点 |
|---|---|---|
| 背包问题 | 15-20题 | 01背包、完全背包、多重背包 |
| 最长上升子序列 | 8-10题 | O(nlogn)优化 |
| 最长公共子序列 | 5-8题 | 二维DP |
| 区间DP | 8-10题 | 枚举顺序 |
| 树形DP | 8-10题 | DFS+DP |
动态规划不是玄学,是模型。
把这5个模型吃透,DP就通了。
📞 想获取《DP模型精选100题》?可添加薛老师微信免费领取。