复赛中最可靠的工作流是:命令行编译运行、用文件重定向测样例、用计时判断复杂度、用对拍抓隐藏错误。
L02 Linux、编译与对拍
把“样例过了”升级成“我能验证代码”:编译、运行、定位崩溃、对拍找反例。
一、先回答问题
先走一遍最小命令流,再看 GDB/sanitizer,最后用 P1048 采药演示正解与暴力对拍。
二、本节知识点地图
| 知识点 | 学生要会什么 | 课堂检查 |
|---|---|---|
| 目录与文件命令 | 会建目录、进目录、列文件 | 现场建 cspj-l02 |
| g++ 编译 | 区分提交版、调试版、sanitizer 版 | 口答参数作用 |
| 重定向与计时 | 会用 in/out 文件重复测试 | 跑一次样例并计时 |
| GDB 基础 | 会 run、print、backtrace | 定位越界小程序 |
| 对拍 | 理解 gen/std/brute/diff | 跑通 P1048 对拍 |
三、概念讲清楚
样例通常只帮助理解题意,不保证正确性。真正的调试顺序是:先能复现错误,再定位错误,再用边界数据或对拍确认修复。
| 问题 | 工具 | 先做什么 |
|---|---|---|
| 不会运行 | g++ + ./main | 先确认能编译、能读入样例 |
| 程序崩了 | GDB / sanitizer | 先定位崩在哪一行 |
| 样例过但 WA | 对拍 | 先找一组具体反例 |
四、由浅入深:三种编译命令
| 场景 | 命令 | 用途 |
|---|---|---|
| 提交/常规测试 | g++ main.cpp -o main -std=c++14 -O2 -Wall | 接近比赛提交环境 |
| GDB 调试 | g++ main.cpp -o main -std=c++14 -g -Wall | 保留行号和变量信息 |
| 查越界 | g++ main.cpp -o main -std=c++14 -g -Wall -fsanitize=address,undefined | 快速发现数组越界、未定义行为 |
五、互动演示:复赛工作流
步骤1
命令mkdir
目的建目录
mkdir cspj-l02
cd cspj-l02
g++ main.cpp -o main -std=c++14 -O2 -Wall
./main < in.txt > out.txt
time ./main < in.txt > out.txt
六、自己选答案
程序 RE,第一步最应该做什么?
0-1 背包怀疑循环方向错,最适合用什么验证?
点击选项后显示反馈。
七、样例精讲:手推对拍
| 文件 | 作用 | 课堂重点 |
|---|---|---|
std.cpp | 准备提交的正解 | 可能有 bug |
brute.cpp | 慢但简单的暴力 | 小数据可靠 |
gen.cpp | 随机小数据生成器 | 能复现 seed |
check.sh | 自动比较输出 | 不同就打印反例 |
八、为什么 P1048 适合对拍
0-1 背包一维 DP,容量必须倒序。代码短,但循环方向很容易写错。
小数据下枚举每个药材选或不选,逻辑简单可靠。让暴力和正解比较,就能抓出隐藏错误。
九、当堂练习与课后习题
| 类型 | 任务 | 要求 |
|---|---|---|
| 当堂练习 | 补全 g++ / 重定向命令 | 能解释每个参数 |
| 当堂练习 | 跑通 P1048 对拍 | 能说出四个文件作用 |
| 蛋*作业 | 命令行跑 P1048/P1540/P1596 | 保留 in/out 文件 |
| 丁*作业 | 保留完整 P1048 对拍文件夹 | 含一次故意改错抓到的反例 |
十、课堂验收
下课前必须答出
1. -O2 和 -g 分别什么时候用? 2. 重定向是什么意思? 3. RE 后怎么定位? 4. 对拍四个文件分别做什么? 5. P1048 为什么倒序?
十一、C++14 核心代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T, M;
cin >> T >> M;
vector<int> t(M + 1), v(M + 1);
for (int i = 1; i <= M; i++) cin >> t[i] >> v[i];
vector<int> dp(T + 1, 0);
for (int i = 1; i <= M; i++) {
for (int j = T; j >= t[i]; j--) {
dp[j] = max(dp[j], dp[j - t[i]] + v[i]);
}
}
cout << dp[T] << "\n";
return 0;
}
读代码重点:0-1 背包容量必须倒序。故意改成正序,对拍通常能马上抓到反例。