last update: 2025.10.28

请在充分理解本文中命令含义后再使用。笔者不为错误使用本文中的脚本/代码造成的任何后果承担责任。

如何编译

推荐使用命令行编译。可以直接使用 g++,也可以通过 make

make 可以通过 export CXXFLAGS="xxx" 设置编译选项。

必须的编译选项:-std=c++14 -O2。此外,题面中其他的编译选项也建议加上。

建议的编译选项:-Wall -Wextra -Wconversion -Wshadow。并且建议仔细阅读编译器给出的 warning

可选的编译选项:-g -fsanitize=address,undefined-D_GLIBCXX_DEBUG。用来查 UB(后者可以查 STL 库内部 UB)但是这样编译出的程序效率无意义,例如后者会将 std::lower_bound 实现换成 $O(n)$ 的,建议慎用。

  • 一般来说,ubsan 不会主动使程序 RE,只会在 stderr 中输出。为了方便对拍可以在编译选项中加入 -fno-sanitize-recover=undefined
  • 有时调试时关闭 O2 可以更快定位错误原因。

此外,如果想查程序瓶颈,可以试一下 -pg 然后跑完程序后运行 gprof <可执行文件名>。但是效果不一定好。

如何运行程序

ulimit 命令可以为当前终端设置系统限制:

$ ulimit -a
real-time non-blocking time  (microseconds, -R) unlimited
core file size              (blocks, -c) 0
data seg size               (kbytes, -d) unlimited
scheduling priority                 (-e) 0
file size                   (blocks, -f) unlimited
pending signals                     (-i) 62391
max locked memory           (kbytes, -l) 2006336
max memory size             (kbytes, -m) unlimited
open files                          (-n) 1024
pipe size                (512 bytes, -p) 8
POSIX message queues         (bytes, -q) 819200
real-time priority                  (-r) 0
stack size                  (kbytes, -s) 8192
cpu time                   (seconds, -t) unlimited
max user processes                  (-u) 62391
virtual memory              (kbytes, -v) unlimited
file locks                          (-x) unlimited

我们可以将 cpu time(-t) 调成题目时限,stack size(-s) 和 virtual memory(-v) 调成题目空限,再运行程序即可。

这样如果爆了限制会直接 kill 掉。注意一个 shell 窗口内 ulimit 限制只能调小不能调大,所以你可能需要每次新开一个 shell 窗口来运行程序。

timeout <TL> <program> 也能起到超时后 kill 某个程序的效果(TL 默认单位为秒),如 timeout 1.5 ./a 会在 1.5s 后强制终止 ./a。注意这里限制的是 real time,好处是支持浮点数。

想要看更清楚的信息?\time --format="%Us [%Ssystime] %MKiB" ./a 即可。 注意这里显示的空间是 maximum resident size(最大常驻集),与程序占用的虚拟内存略有不同。

根据 官网文档(见 NOI评测系统Atbiter单机版使用介绍.pdf),评测时的内存限制为「虚拟内存」。

set -e 可以使脚本在任意子进程以非 0 值退出时以非 0 值退出。

如何 check?

spj 题手写一个 checker,通过返回值判断是否合法。

普通题目用 diff 即可;如果想忽略空白字符可以使用 diff -Z -B,即忽略行末空格和 全部 空行 (有一定风险)。忘了参数什么意思可以 man diff 或者 diff --help

如何循环?

for 循环:

for i in {1..10}; do
  # do something
done

while 循环:

while [[ true ]]; do
  # run program
  if [[ $? -ne 0 ]]; then
    break
  fi
done

成品

适用于 ZJ 的 Linux 目录结构(每道题的源代码放置在主文件夹下的对应子文件夹内)

~/run.sh:

#!/bin/bash

set -e

cd ~/$1

cp ../down/$1/$1$2.in $1.in

echo "Running on $2..."

ulimit -v 512000
ulimit -s 512000

\time --format="exit %x, %Us [%S sys time] %MKiB" ./$1

diff -B -Z $1.out ../down/$1/$1$2.ans

echo "Passed."

~/eval.sh:

#!/bin/bash

cd ~

make $1/$1

if [[ $? -ne 0 ]]; then
  echo "CE."
  exit 1
fi

for ((i=1;i<=$2;i++)); do
  ./run.sh $1 $i
  if [[ $? -ne 0 ]]; then
    echo "Failed on $i."
    exit 2
  fi
done

echo "Pretests passed.($2)"

用法:~/eval.sh ${problem} ${pretest_count}

标签: none

添加新评论