← 返回首页

NOIP 2023真题解析:动态规划专题

NOIP 2023真题解析:动态规划专题
动态规划:背包问题从入门到精通

核心概念讲解

**动态规划(DP)**的核心思想是 “把大问题拆成小问题,用小问题的答案拼出大问题的解”。它有两个关键性质:

  1. 最优子结构:大问题最优解包含小问题最优解(比如背包最大价值由子背包决定)

  2. 无后效性:当前状态只与历史有关,与未来无关

以背包为例,定义 dp[j] 表示背包容量为 j 时的最大价值。转移方程如下:

$$

dp[j] = \max(dp[j], dp[j - w[i]] + v[i])

$$

其中 w[i] 是第 i 件物品的重量,v[i] 是它的价值。


完全背包 vs 01背包

完全背包

  • 特点:每种物品可无限取

  • 关键区别:内层循环采用 正序遍历,确保每个物品可以被多次选取


for (int j = w[i]; j <= W; ++j) { // 正序

dp[j] = max(dp[j], dp[j-w[i]] + v[i]);

}

01背包

  • 特点:每种物品最多选一次

  • 关键区别:内层循环采用 逆序遍历,防止同一物品被重复计数


for (int j = W; j >= w[i]; --j) { // 逆序

dp[j] = max(dp[j], dp[j-w[i]] + v[i]);

}

C++代码示例


#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1005;       // 最大物品数量

const int MAXW = 2000005;    // 背包容量上限(根据实际题目调整)

struct Item {

int weight;

int value;

} items[MAXN];

// dp数组初始化:所有元素初始化为0(默认空背包价值为0)

int dp[MAXW];

void knapsack_complete(int n, int W) {

for (int i = 1; i <= n; ++i) {          // 遍历物品

for (int j = items[i].weight; j <= W; ++j) { // 正序遍历背包容量

if (dp[j - items[i].weight] + items[i].value > dp[j]) {

dp[j] = dp[j - items[i].weight] + items[i].value;

}

}

}

}

void knapsack_01(int n, int W) {

for (int i = 1; i <= n; ++i) {          // 遍历物品

for (int j = W; j >= items[i].weight; --j) { // 逆序遍历背包容量

if (dp[j - items[i].weight] + items[i].value > dp[j]) {

dp[j] = dp[j - items[i].weight] + items[i].value;

}

}

}

}

int main() {

int n, W;

cin >> n >> W;

for (int i = 1; i <= n; ++i) {

cin >> items[i].weight >> items[i].value;

}

// 清空dp数组(显式初始化)

memset(dp, 0, sizeof(dp));

cout << "完全背包最大价值: ";

knapsack_complete(n, W);

cout << dp[W] << endl;

cout << "01背包最大价值: ";

knapsack_01(n, W);

cout << dp[W] << endl;

return 0;

}

注释要点

  1. 使用 items 结构体提高可读性

  2. dp 数组显式初始化为 0

  3. 两种背包问题分别演示正序/逆序遍历

  4. 输入输出格式清晰


算法分析

  • 时间复杂度:O(N×W),N 是物品数,W 是背包容量

  • 空间复杂度:O(W),一维数组优化只需记录当前状态

  • 常见误区

  • 完全背包误用逆序遍历(导致物品只能选一次)

  • 忘记初始化 dp[0] = 0

  • 边界条件处理不当(如物品重量超过背包容量)


经典例题解析

例题1:P1060 [NOIP2005普及组] 采药

  • 题意:给定预算和若干药材(每种最多选一次),求不超过预算的最大价值

  • 关键点

  • 属于 01背包

  • 需将重量和价值分开存储,注意输入顺序

例题2:P1048 [NOIP2006普及组] 开心的金明

  • 题意:给定预算和若干物品(每种最多选一次),求不超过预算的最大价值

  • 对比分析

  • 同属 01背包,但本题有额外约束条件(必须选至少一个物品)

  • 需在 DP 后检查 dp[W] 是否为 0


推荐练习

  1. P1060 采药 (巩固 01背包)

  2. P1079 [NOIP2004] 虫虫危机 (二维 DP 入门)

  3. P1069 [NOIP2004] 细胞分裂 (状态压缩 DP 基础)


小结

动态规划的精髓是 “状态定义+转移方程”,记住两点:

  1. 状态要涵盖所有必要信息(如 dp[j] 必须包含容量 j 的信息)

  2. 转移要确保不漏解(背包问题需同时考虑不选/选当前物品的情况)

下期预告:树形 DP——解决树上路径问题的利器!