【CSP】CSP-XL 2025辽宁复赛真题-第四题, 购物(buy)
CSP-XL 2025辽宁复赛真题-第四题,购物(buy),也是本次考试最后一题,难度最大,DFS思想考点,属于GESP六级考纲内容,难度⭐⭐⭐☆☆。
CSP-XL 2025辽宁复赛真题-第四题, 购物(buy)
题目要求
题目分析
1.问题抽象
这是一个组合计数问题,本质是:
- 决策树问题:每个物品是一个决策点,每个决策点有 2 个分支
- 状态空间搜索:需要遍历所有可能的组合状态
- 约束满足问题:在满足
sum ≤ s的约束下计数
2.问题特征识别
当我们看到以下特征时,应该考虑DFS回溯:
- ✅ 需要枚举所有可能的组合/排列
- ✅ 每个位置有多个选择
- ✅ 需要统计满足条件的方案数
- ✅ 数据规模相对较小
3.思维模型:决策树
想象一棵决策树:
1
2
3
4
5
6
[开始: sum=0]
/ \
[选物品1选项0: sum=2] [选物品1选项1: sum=3]
/ \ / \
[选物品2选项0] [选物品2选项1] [选物品2选项0] [选物品2选项1]
sum=3 ✓ sum=6 ✗ sum=4 ✓ sum=7 ✗
关键理解:
- 每个节点代表一个状态(当前已做的选择)
- 每条边代表一个决策(选择哪个选项)
- 叶子节点代表一个完整方案
4.算法的三个核心步骤
步骤1:尝试(Make Choice)
1
sum += a[count][i]; // 做出选择:累加当前选项的值
- 在当前决策点,尝试一个选项
- 更新状态(sum)
步骤2:递归(Recurse)
1
dfs(count + 1); // 递归处理下一个决策点
- 基于当前选择,继续处理后续决策
- 这是分治思想:把大问题分解为子问题
步骤3:回溯(Undo Choice)
1
sum -= a[count][i]; // 撤销选择:恢复状态
- 撤销当前选择,恢复状态
- 准备尝试下一个选项
4.完整执行示例
假设 n=2, s=5,数据:
- 物品1: [2, 3]
- 物品2: [1, 4]
执行流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
调用栈和状态变化:
1. dfs(0) - count=0, sum=0
├─ 尝试选项0: sum = 0 + 2 = 2
│ └─ dfs(1) - count=1, sum=2
│ ├─ 尝试选项0: sum = 2 + 1 = 3
│ │ └─ dfs(2) - count=2, sum=3
│ │ └─ 终止条件:count == n
│ │ └─ 检查:sum=3 ≤ 5 ✓,ans++ (ans=1)
│ │ └─ 返回
│ │ └─ 回溯:sum = 3 - 1 = 2
│ │
│ └─ 尝试选项1: sum = 2 + 4 = 6
│ └─ dfs(2) - count=2, sum=6
│ └─ 终止条件:count == n
│ └─ 检查:sum=6 > 5 ✗,不计数
│ └─ 返回
│ └─ 回溯:sum = 6 - 4 = 2
│ └─ 返回
│ └─ 回溯:sum = 2 - 2 = 0
│
└─ 尝试选项1: sum = 0 + 3 = 3
└─ dfs(1) - count=1, sum=3
├─ 尝试选项0: sum = 3 + 1 = 4
│ └─ dfs(2) - count=2, sum=4
│ └─ 终止条件:count == n
│ └─ 检查:sum=4 ≤ 5 ✓,ans++ (ans=2)
│ └─ 返回
│ └─ 回溯:sum = 4 - 1 = 3
│
└─ 尝试选项1: sum = 3 + 4 = 7
└─ dfs(2) - count=2, sum=7
└─ 终止条件:count == n
└─ 检查:sum=7 > 5 ✗,不计数
└─ 返回
└─ 回溯:sum = 7 - 4 = 3
└─ 返回
└─ 回溯:sum = 3 - 3 = 0
└─ 返回
最终:ans = 2
记住:DFS回溯的本质是系统性地尝试所有可能,通过回溯保证不遗漏、不重复。
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
long long a[25][2]; // a[i][0] 表示第 i 件物品方案一的花费,a[i][1] 表示方案二的花费
long long sum = 0; // 当前已选物品的总花费
long long n, S; // n 件物品,预算上限为 S
long long ans = 0; // 记录总花费不超过 S 的合法方案数
// 深度优先搜索,count 表示当前正在决策第 count 件物品(0-based)
void dfs(int count) {
if (count == n) { // 所有物品决策完毕
if (sum <= S) { // 若总花费不超预算,则方案合法
ans++;
}
return;
}
// 对第 count 件物品,分别尝试“不选”与“选”两种选择
for (int i = 0; i <= 1; i++) {
sum += a[count][i]; // 累加当前选择的花费
dfs(count + 1); // 递归决策下一件物品
sum -= a[count][i]; // 回溯,撤销当前选择的花费
}
return;
}
int main() {
freopen("buy.in", "r", stdin); // 按复赛要求重定向输入
freopen("buy.out", "w", stdout); // 按复赛要求重定向输出
std::cin >> n >> S; // 读入物品件数 n 与预算上限 S
for (int i = 0; i < n; i++) {
std::cin >> a[i][0] >> a[i][1]; // 读入每件物品不选/选的花费
}
dfs(0); // 从第 0 件物品开始搜索
std::cout << ans; // 输出合法方案总数
return 0;
}
附:样例和测试数据下载地址:
链接:https://pan.quark.cn/s/51ac09b0b260?pwd=DRDt 提取码:DRDt
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP 学习专题站:GESP WIKI
“luogu-”系列题目可在洛谷题库进行在线评测。
“bcqm-”系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助
本文由作者按照 CC BY-NC-SA 4.0 进行授权


