有关对拍的程序
感谢 C++ 思路:
OI 中都有哪些对拍的方法? - ruierqwq 的回答 - 知乎
https://www.zhihu.com/question/433356711/answer/1611183740
以下所讲的对拍都是 Windows 环境。Linux 环境可以稍作修改。
一个很简单的 Windows 下命令行对拍
@echo off
g++ data.cpp -o data.exe -Wall
g++ sol.cpp -o sol.exe -Wall
g++ user.cpp -o user.exe -Wall
:loop
data.exe > data.in
sol.exe < data.in > sol.out
user.exe < data.in > user.out
fc sol.out user.out
if not errorlevel 1 goto loop
fc sol.out user.out > data.log
pause
:end
其中 data.exe
、sol.exe
和 user.exe
分别为数据生成程序,暴力程序 / 标程,用户程序,均不需要开启文件输入输出。
会一键编译三个程序,然后循环运行三个程序,将答案输出与用户输出比对。若无错就继续循环,否则将日志输出到文本文档,按任意键后结束。
其中存在一些问题就是,很多的比对无错会比较难看。于是考虑写进 C++ 的程序里。
C++ 对拍程序
通过 system ()
函数,我们可以在 C++ 程序里调用系统函数。以下程序为 Windows 下的命令,Linux 下请做如下更改:
data.exe > data.in
→./data > data.in
sol.exe < data.in > sol.out
→./sol.exe < data.in > sol.out
user.exe < data.in > user.out
→./user.exe < data.in > user.out
fc sol.out user.out > data.log
→diff sol.out user.out > data.log
Click to show code
#include <bits/stdc++.h>
using namespace std;
signed main () {
printf ("Compiling data.cpp ...\n");
system ("g++ data.cpp -o data.exe -Wall");
printf ("Compiling sol.cpp ...\n");
system ("g++ sol.cpp -o sol.exe -Wall");
printf ("Compiling user.cpp ...\n");
system ("g++ user.cpp -o user.exe -Wall");
int st, ed, cst;
int cnt = 1;
while (1) {
system ("data.exe > data.in");
system ("sol.exe < data.in > sol.out");
system ("user.exe < data.in > user.out");
if (system ("fc sol.out user.out > data.log")) {
printf ("Wrong Answer on test #%d\n", cnt);
break;
} else printf("Accepted on test #%d\n", cnt);
cnt++;
}
system ("pause");
exit (0);
}
接下来,我们可以加入计时系统,统计数据生成、暴力、用户程序所需要的时间。
通过使用 clock ()
来完成计时。
具体的,
int st, ed, cst;
st = clock (); // 记录开始时间
// 你要运行的东西
ed = clock (); // 记录停止时间
cst = ed - st; // 运行时间 = 停止时间 - 开始时间
printf ("%dms\n", cst); // 输出时间
Click to show code
#include <bits/stdc++.h>
using namespace std;
signed main () {
printf ("Compiling data.cpp ...\n");
system ("g++ data.cpp -o data.exe -Wall");
printf ("Compiling sol.cpp ...\n");
system ("g++ sol.cpp -o sol.exe -Wall");
printf ("Compiling user.cpp ...\n");
system ("g++ user.cpp -o user.exe -Wall");
int st, ed, cst;
int cnt = 1;
while (1) {
st = clock ();
system ("data.exe > data.in");
ed = clock ();
cst = ed - st;
printf ("data: %dms\n", cst);
st = clock ();
system ("sol.exe < data.in > sol.out");
ed = clock ();
cst = ed - st;
printf ("sol: %dms\n", cst);
st = clock ();
system ("user.exe < data.in > user.out");
ed = clock ();
cst = ed - st;
printf ("user: %dms\n", cst);
if (system ("fc sol.out user.out > data.log")) {
printf ("Wrong Answer on test #%d\n", cnt);
break;
} else printf("Accepted on test #%d\n", cnt);
cnt++;
}
system ("pause");
exit (0);
}
完整但是不建议考试时使用的程序
加了检查程序文件是否存在。加了一些可选项。可以改数据程序、答案程序和用户程序的名字,切换是否编译,切换是否显示时间。考试的时候并不建议使用,搞这么一大串改来改去的可选项很浪费时间。
Click to show code
#include <bits/stdc++.h>
using namespace std;
/*------------------------------------*/
/*--- Don't touch anything above!! ---*/
/*------------------------------------*/
const char* CONFIG[] = {
"data.cpp", // 你的生成数据的程序
"sol.cpp", // 答案程序 / 暴力程序
"user.cpp", // 你的程序
"1", // 是否编译
"1", // 是否计时
};
/*------------------------------------*/
/*--- Don't touch anything below!! ---*/
/*------------------------------------*/
#define data CONFIG[0]
#define sol CONFIG[1]
#define user CONFIG[2]
#define comp CONFIG[3] == "1"
#define timi CONFIG[4] == "1"
void checkfileifNexist (const char* str);
signed main () {
checkfileifNexist (data);
checkfileifNexist (sol);
checkfileifNexist (user);
// ------------------------------------
char ctmp[127];
if (comp) {
printf ("compiling %s...\n", data);
sprintf (ctmp, "g++ %s -o data.exe -Wall", data);
const char* compdata = ctmp;
system (compdata);
printf ("compiling %s...\n", sol);
sprintf (ctmp, "g++ %s -o sol.exe -Wall", sol);
const char* compsol = ctmp;
system (compsol);
printf ("compiling %s...\n", user);
sprintf (ctmp, "g++ %s -o user.exe -Wall", user);
const char* compuser = ctmp;
system (compuser);
}
int st, ed, cst;
int cnt = 1;
while (1) {
if (timi) st = clock ();
system ("data.exe > data.in");
if (timi) ed = clock ();
if (timi) cst = ed - st;
if (timi) printf ("data: %dms\n", cst);
if (timi) st = clock ();
system ("sol.exe < data.in > sol.out");
if (timi) ed = clock ();
if (timi) cst = ed - st;
if (timi) printf ("sol: %dms\n", cst);
if (timi) st = clock ();
system ("user.exe < data.in > user.out");
if (timi) ed = clock ();
if (timi) cst = ed - st;
if (timi) printf ("user: %dms\n", cst);
if (system ("fc sol.out user.out > data.log")) {
printf ("Wrong Answer on test #%d\n", cnt);
break;
} else printf("Accepted on test #%d\n", cnt);
cnt++;
}
system ("pause");
exit (0);
}
void checkfileifNexist (const char* str) {
FILE* fp = fopen (str, "r");
if (fp == NULL) {
// not exist, or you don't \
have read permission.
printf ("data maker program \"");
printf ("%s\" not found.\n", str);
printf ("not exist, or you don't");
printf (" have read permission.\n");
printf ("press any key to leave.\n");
system ("pause");
exit (0);
} else fclose (fp);
}
一些对拍的奇怪使用场合
我们可以用对拍来造数据。两回了两回,我发现以前同学的数据做的有错误,然后我自己的数据生成器生成的数据也不保证合法,我就会采用对拍的方法来查验数据是否合法。
我不是特别擅长这些命令行 QAQ 所以有一些麻烦的地方请谅解
@echo off
echo 编译:data.cpp
g++ data.cpp -o data.exe -Wall
echo 编译:user.cpp
g++ user.cpp -o user.exe -Wall
echo 编译:sol.cpp
g++ sol.cpp -o sol.exe -Wall
echo 全部源代码编译完毕
:loop
data.exe > data.in
user.exe < data.in > user.out
fc user.out blank.txt
if not errorlevel 1 goto loop
sol.exe < data.in > sol.out
fc user.out sol.out
if errorlevel 1 goto loop
echo 哈哈终于有一组合格的数据了
pause
:end
前提条件:你的 user.cpp
必须保证正确,可以是一直和之前对拍时 sol.cpp
输出一致的程序,也可以直接是 sol.cpp
改个文件名写进 user.cpp
。
和 OI 中对拍的不同之处:user.cpp
和 sol.cpp
需要变量的范围不同,保证数据的处理过程中不爆 int
或者不爆 long long
等,高精度可以无视。
最前面是编译三个文件。可以选择删掉一部分或者全部。
然后进行合法性检测:只运行 data.exe
和 user.exe
,将 user.out
与 blank.txt
进行比对,若相同则证明数据不合法,blank.txt
可以是一个空的文本文档,或者是你不想生成但是大量出现的结果;
然后进行运算范围检测:若有运算时数据范围的限制,如「题目保证中间计算过程以及结果均不超过 \(2^{31}−1\)」之类文字,则运行 sol.exe
,将 sol.out
与 user.out
进行比对,若不同就是一个爆了数据范围,另一个没爆(两者变量范围应当不同,见上,使用 #define
解决),可见数据不合法。否则可删除这些条;
通过所有检测之后你的数据就合法了,然后手动复制,拖到一个存放数据的文件夹里,重命名 dataX.in
,dataX.out
。
没这么用之前从来没想过对拍能这么用