本地测试与调试

为什么要本地测

评测机是排队串行的。高峰期一次提交要等几分钟,你交一次、等三分钟、发现 WA、改一个字、再等三分钟——这个循环一上午能毁掉你三道题的时间。

本地先跑通样例,是基本的效率要求,不是可选项。

输入输出重定向

手动敲输入太慢,尤其是多组数据。用文件重定向:

#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif

ONLINE_JUDGE 宏在评测机上被自动定义,所以这段只在本地生效。

Danger

提交前务必确认这段没在评测机上生效。 如果评测机没定义这个宏而你又写了 freopen,程序会去读一个不存在的文件,结果是全线 RE。交之前扫一眼,这个习惯养成了就不出事,没养成会反复出事。

常用优化开关

数据量到 10⁵ 以上时,cin/cout 会明显拖慢:

ios::sync_with_stdio(false);
cin.tie(nullptr);

Warning

开了这个之后不要混用 scanf/printf 和 cin/cout,两者的缓冲区不同步,输出顺序会乱,而且这种乱是随机出现的,你会查很久。

卡住了怎么定位

本地过、提交 WA

按这个顺序查,别跳:

  1. 输出格式 —— 行末多余空格、少一个换行、大小写、Case #1: 里的冒号。
  2. 多组数据的读法 —— 题目是「读到 EOF」还是「第一行给 T」?读错了有时候本地样例刚好能过。
  3. 数组开小 —— 本地数据弱,没炸;评测机数据强,就炸了。
  4. 整数溢出 —— 中间结果超过 INT_MAX(2.1×10⁹)要开 long long。

这四类占新手 WA 的九成以上。不要一 WA 就怀疑算法,先排这四条。

TLE

先算复杂度,再改代码。别一上来就优化常数。

n 的范围 1 秒内可行的复杂度
n ≤ 10 O(n!) 看运气
n ≤ 20 O(2ⁿ)
n ≤ 500 O(n³)
n ≤ 5000 O(n²)
n ≤ 10⁶ O(n log n)
n ≤ 10⁸ O(n)

复杂度对了还超时,再考虑常数(换快速 IO、去掉 vector 的边界检查、把 cout 换成 printf)。

输出量大的时候,printf 明显比 cout 快。 cout 默认和 C 的 stdio 同步,每次输出都有额外开销;循环里输出十万行时,这个差别就是「过」和「超时」。这也是讲义统一用 printf 的原因之一。

RE

两个最常见的原因:

  • 递归爆栈 —— 本地默认栈 8MB,深度递归要手动调。
  • 数组越界 —— 本地数据弱,越界的那块内存刚好没被占用,看起来正常。

开 sanitizer 直接抓:

g++ -std=c++17 -fsanitize=address,undefined -g a.cpp -o a
./a

它会在越界或未定义行为发生的那一行报错并退出,比对着 WA 猜快得多。

Info

sanitizer 会让程序慢 2–5 倍、内存占用增加,所以只用它找错,不要用它的耗时判断会不会 TLE。

对拍

怀疑算法对但边界错时,写个暴力程序对拍,比人眼找一小时快:

for i in $(seq 1 1000); do
    python3 gen.py > in.txt
    ./brute < in.txt > out1.txt
    ./mine  < in.txt > out2.txt
    if ! diff -q out1.txt out2.txt > /dev/null; then
        echo "WA on case $i"
        cat in.txt
        break
    fi
done

关键在 gen.py:要能生成覆盖边界的随机数据(最小值、最大值、全相同、递增递减、n=1)。随手写的随机生成器经常漏掉边界,对吧半天对不出来。

数组开多大

按题目上限再加个余量,别刚好卡边界:

const int N = 1e5 + 10;
int a[N];

刚好开 1e5 而代码里有 a[n + 1],越界。这个 + 10 是买保险,不是浪费。

© HiJudge · 内容采用 CC BY-NC-SA 4.0 许可协议            此页面修订于: 2026-11-01 03:44:11

results matching ""

    No results matching ""