L02 Linux、编译与对拍

把“样例过了”升级成“我能验证代码”:编译、运行、定位崩溃、对拍找反例。

一、先回答问题

结论

复赛中最可靠的工作流是:命令行编译运行、用文件重定向测样例、用计时判断复杂度、用对拍抓隐藏错误。

教学安排

先走一遍最小命令流,再看 GDB/sanitizer,最后用 P1048 采药演示正解与暴力对拍。

二、本节知识点地图

知识点学生要会什么课堂检查
目录与文件命令会建目录、进目录、列文件现场建 cspj-l02
g++ 编译区分提交版、调试版、sanitizer 版口答参数作用
重定向与计时会用 in/out 文件重复测试跑一次样例并计时
GDB 基础会 run、print、backtrace定位越界小程序
对拍理解 gen/std/brute/diff跑通 P1048 对拍

三、概念讲清楚

Guide to CP 的调试习惯

样例通常只帮助理解题意,不保证正确性。真正的调试顺序是:先能复现错误,再定位错误,再用边界数据或对拍确认修复。


问题工具先做什么
不会运行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

六、自己选答案

问题 1

程序 RE,第一步最应该做什么?

问题 2

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 背包容量必须倒序。故意改成正序,对拍通常能马上抓到反例。