首先是之前一直在用的多线程对拍,非常强大(临近正式考试就不要用了,太强大了容易产生依赖)
可以检测 RE、AC、MLE、TLE、WA
先把代码挂上
点击查看代码
#include <bits/stdc++.h>
using namespace std;
void Black() { printf("\033[37m"); }
void Red() { printf("\033[31m"); }
void Green() { printf("\033[32m"); }
void Yellow() { printf("\033[33m"); }
void Blue() { printf("\033[34m"); }
void Purple() { printf("\033[35m"); }
void DeepGreen() { printf("\033[36m"); }
void White() { printf("\033[37m"); }
int sys(string cmd) { return system(cmd.c_str()); }
#define nnow steady_clock::now()
#define dc duration_cast<duration<double>>(t2 - t1)
#define time ut.count()
#define pc putchar
#define pr printf
#define ps puts
#define Wall ps("--------------------------------------------")
#define CE(x) Re(), ps("Error"), Bl(), Wall, exit(1);
#define COM(x) Pu(), pr("Compile " #x " "), Bl();
#define WA() Wh(), pr("%03d ", i), Re(), ps("Wrong Answer"), Bl(), exit(1);
#define Bls "\033[37m"
#define Res "\033[31m"
#define Grs "\033[32m"
#define Yes "\033[33m"
#define BLs "\033[34m"
#define Pus "\033[35m"
#define DGs "\033[36m"
void BLK() { pr("\033[30m"); }
void Bl() { pr("\033[37m"); }
void Re() { pr("\033[31m"); }
void Gr() { pr("\033[32m"); }
void Ye() { pr("\033[33m"); }
void BL() { pr("\033[34m"); }
void Pu() { pr("\033[35m"); }
void DG() { pr("\033[36m"); }
void Wh() { pr("\033[37m"); }
void Bo() { pr("\033[1m"); }
#include <thread>
#include <mutex>
typedef long long ll;
using namespace std;
using namespace chrono;
string args[10], targ[10];
int cnt;
bool no_com = 0, out_ac = 0, qstop = 0;
int cas = 1000; double t = 1000; bool fast = 0; bool fast2 = 0;
int mem = 128 * 1024; int Max = 10;
queue<thread> q;
bool Compile(string filename, string tf) {
if (sys("g++ " + tf + ".cpp -o " + filename + " -Wall -Wno-unused-result -DDEBUG -DDP " + (fast ? " -O2" : (fast2 ? "" : " -O2 -fsanitize=address,undefined")))) return Bo(), 0;
Bo(), Gr(), ps("Done"), Bl();
return 1;
}
mutex m;
double cpp_tot = 0, std_tot = 0, rd_tot = 0;
ll cpp_mem = 0, std_mem = 0, rd_mem = 0;
int cas_tot = 0, unac_tot = 0;
bool Test(int i) {
char s = 'P', s2 = 'P';
double cpp = NAN, std = NAN, rd = NAN;
int cppm = -1, stdm = -1, rdm = -1;
FILE * fe; int ret; char tmp[34];
// run rand
string tm = (string) "./data/tm" + to_string(i).c_str() + ".log";
ret = sys("ulimit -s " + to_string(mem + 100) + " && /usr/bin/time -f \"%e %M\" -o " + tm + " ./" + args[3] + " > ./data/rd" + to_string(i));
if (ret) goto out;
fe = fopen(tm.c_str(), "r"); fscanf(fe, "%lf%d", &rd, &rdm); rd *= 1000; fclose(fe);
// run cpp
ret = sys("ulimit -s " + to_string(mem + 100) + " && timeout 2> sb " + to_string(t / 1000 + 0.3) + " /usr/bin/time -f \"%e %M\" -o " + tm + " ./" + args[1] + " < ./data/rd" + to_string(i) + " > ./data/cpp" + to_string(i));
if (ret == 31744) { s = 'T'; goto out; }
if (ret == 35584) { s = 'M'; goto out; }
if (ret) { s = 'R'; goto out; }
fe = fopen(tm.c_str(), "r"); fscanf(fe, "%lf%d", &cpp, &cppm); cpp *= 1000; fclose(fe);
if (cpp >= t) { s = 'T'; goto out; }
if (cppm > mem) { s = 'M'; goto out; }
// run std
ret = sys("ulimit -s " + to_string(mem + 100) + " && timeout 2> sb " + to_string(t / 1000 + 0.3) + " /usr/bin/time -f \"%e %M\" -o " + tm + " ./" + args[2] + " < ./data/rd" + to_string(i) + " > ./data/std" + to_string(i));
if (ret == 31744) { s2 = 'T'; goto out; }
if (ret == 35584) { s2 = 'M'; goto out; }
if (ret) { s2 = 'R'; goto out; }
fe = fopen(tm.c_str(), "r"); fscanf(fe, "%lf%d", &std, &stdm); std *= 1000; fclose(fe);
if (std >= t) { s2 = 'T'; goto out; }
if (stdm > mem) { s2 = 'M'; goto out; }
// diff
if (sys("diff -Z ./data/cpp" + to_string(i) + " ./data/std" + to_string(i) + " > ./data/diff.log")) s = s2 = 'W';
else s = s2 = 'A';
// output
out:
lock_guard<mutex> Lock(m);
++cas_tot;
if (s != 'A') ++unac_tot;
if (s == 'A') cpp_tot += cpp, std_tot += std, rd_tot += rd, cpp_mem += cppm, std_mem += stdm, rd_mem += rdm;
if (s != 'A' || out_ac) {
if (s == 'A') Gr();
else if (s == 'T' || s2 == 'T') Ye();
else if (s == 'R' || s2 == 'R') Pu();
else if (s == 'M' || s2 == 'M') BLK();
else if (s == 'W') Re();
pr("%03d ", i);
putchar(s), putchar(' '), putchar(s2);
pr(" %.0lfms %.0lfms %.0lfms %dMB %dMB %dMB\n", rd, cpp, std, rdm / 1024, cppm / 1024, stdm / 1024), Bl();
if (qstop && s != 'A') { Wall, abort(); }
}
if (s != 'P' && s2 != 'P') sys(("rm " + tm).c_str());
if (s != 'P') sys("rm ./data/cpp" + to_string(i));
if (s2 != 'P') sys("rm ./data/std" + to_string(i));
if (s == 'A') sys("rm ./data/rd" + to_string(i));
return 0;
}
int main(int argv, char **argc) {
Bo(), sys("rm -f ./data/*");
for (int i = 1; i < argv; ++i) {
string tmp = argc[i];
if (tmp == "-n") no_com = 1;
// no_com complie
else if (tmp == "-l") ++i, cas = stoi(string(argc[i]));
// testcases
else if (tmp == "-t") ++i, t = stoi(string(argc[i]));
// time limit
else if (tmp == "-f") fast = 1;
// fast mode (no_com fsanitize)
else if (tmp == "-ff") fast2 = 1;
// fast2 mode (no_com O2 & fsanitize)
else if (tmp == "-m") ++i, mem = stoi(string(argc[i])) * 1024;
// memory limit
else if (tmp == "-a") out_ac = 1;
// echo accept message
else if (tmp == "-s") qstop = 1;
// immediately stop when unaccepted
else if (tmp == "-c") ++i, Max = stoi(string(argc[i]));
// max number of threads
else args[++cnt] = argc[i];
}
targ[1] = args[1], targ[2] = args[2], targ[3] = args[3];
pr("%scpp: %s%s.cpp\n", Pus, BLs, args[1].c_str());
pr("%sstd: %s%s.cpp\n", Pus, BLs, args[2].c_str());
pr("%srand: %s%s.cpp\n", Pus, BLs, args[3].c_str());
if (fast) pr("%sFast Mode %sOn\n", Pus, BLs), pr("%sFsanitize %sDisabled\n", Pus, BLs);
else if (fast2) pr("%sFast II Mode %sOn\n", Pus, BLs), pr("%sO2 %sDisabled\n", Pus, BLs), pr("%sFsanitize %sDisabled\n", Pus, BLs);
if (no_com) pr("%sCompile %sDisabled\n", Pus, BLs);
pr("%sTest Count: %s%d\n", Pus, BLs, cas);
pr("%sThread Number: %s%d\n", Pus, BLs, Max);
pr("%sTime Limit: %s%dms\n", Pus, BLs, int(t));
pr("%sMemory Limit: %s%dMB\n", Pus, BLs, mem / 1024), Bl();
Wall;
if (!no_com) {
COM(cpp); if (!Compile(args[1], targ[1])) CE(cpp);
COM(std); if (!Compile(args[2], targ[2])) CE(std);
COM(rand); if (!Compile(args[3], targ[3])) CE(rand);
}
Pu(), ps("Now Begin Test"), Bl(), Wall;
steady_clock::time_point t1, t2; duration<double> ut;
t1 = nnow;
for (int i = 1; i != (cas + 1); ++i) {
if ((int) q.size() > Max) q.front().join(), q.pop();
q.push(thread(Test, i));
if (i % 100 == 0) {
lock_guard<mutex> Lock(m);
int ac = cas_tot - unac_tot;
printf("%s%d %sCases, %s%d %sWrongs, %s%.2lf%%%s\n", BLs, cas_tot, Pus, BLs, unac_tot, Pus, BLs, ac * 1.0 / cas_tot * 100, Pus);
t2 = nnow; ut = dc;
printf("Total: %s%.3lfs\n", BLs, ut.count());
printf("%sAverage: %s%.0lfms %.0lfms %.0lfms %.0lfMB %.0lfMB %.0lfMB\n", Pus, BLs, rd_tot / ac, cpp_tot / ac, std_tot / ac, 1.0 * rd_mem / ac / 1024, 1.0 * cpp_mem / ac / 1024, 1.0 * std_mem / ac / 1024), Bl();
Wall;
}
}
while (!q.empty()) q.front().join(), q.pop();
t2 = nnow; ut = dc;
int ac = cas_tot - unac_tot;
printf("%s%d %sCases, %s%d %sWrongs, %s%.2lf%%%s\n", BLs, cas_tot, Pus, BLs, unac_tot, Pus, BLs, ac * 1.0 / cas_tot * 100, Pus);
printf("Total: %s%.3lfs\n", BLs, ut.count());
printf("%sAverage: %s%.0lfms %.0lfms %.0lfms %.0lfMB %.0lfMB %.0lfMB\n", Pus, BLs, rd_tot / ac, cpp_tot / ac, std_tot / ac, 1.0 * rd_mem / ac / 1024, 1.0 * cpp_mem / ac / 1024, 1.0 * std_mem / ac / 1024), Bl();
Bl(), Wall;
sys("rm -f ./data/.f*");
Gr(), ps("Finished"), Bl(), Wall;
}
首先先把它编译了,记得加一句 -pthread,要不然编译不了(因为有多线程)
类似于这样一句:g++ duipai.cpp -o dp -O2 -pthread
然后我们就假设你编译出来的程序名叫 dp
然后用法就是 ./dp {自己的代码文件名} {正解/暴力代码文件名} {数据生成器代码文件名}
这几个文件要求是 .cpp 格式的,但是输入的时候不加 .cpp,然后代码里面不要有 freopen
然后它会给你自动编译代码并执行
哦对,用之前得现在当前文件夹下建一个名字叫 data 的文件夹,到时候拍出错的数据可以在里面找
比如这是我用来测试的一组代码:
自己的代码 (文件是 cpp.cpp)
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <chrono>
int main() {
int x;
scanf("%d", &x);
if (x == 0) puts("AC");
else if (x == 1) while (1);
else if (x == 2) abort();
else puts("WA");
}
标程的代码 (文件是 std.cpp)
#include <cstdio>
int main() { puts("AC"); }
数据生成器 (文件是 rd.cpp)
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <chrono>
int main() {
srand(std::chrono::system_clock::now().time_since_epoch().count());
int x = rand() % 4;
printf("%d\n", x);
}
那我对拍时候就这样用: ./dp cpp std rd
出来效果是这样:
这里只显示了没有 AC 的测试点)如果要显示 AC 可以看看等下下面的一些附加参数
第一列那个数组是测试点编号
第二列是自己代码的运行状态(应该能看懂吧,结合颜色)
第三列是正解代码的运行状态(P 就代表自己的代码运行有问题,这一组直接跳了)
第四列是数据生成器的运行时间
第五列是自己代码的运行时间 (如果运行时有问题就是 nan)
第六列是正解代码的运行时间
第七列是数据生成器的运行内存
第八列、第九列是自己、正解代码的运行内存
然后每隔大约一百个测试点就会输出一次均值:
然后没有 AC 的数据可以在刚刚建的文件夹里面找,错的是第几个测试点数据就叫 rdxxx
如果要强制退出就按 ctrl-z (如果没用就只能关终端重开了)
然后是一些参数
首先是可以设置一共测试多少组,只要加一个 -l xxx
就行
比如我要测 100 组我就可以 ./dp cpp std rd -l 100
,这里默认是 1000 组(由于有多线程所以一般很快就能跑完)
注意 -xxx
可以加在任何位置,只要保证三个代码不带 -
并且按顺序输入就行。
比如我可以 ./dp cpp -l 100 std rd
(正常人应该不会这样写吧)
然后是可以设置是否显示 AC 的数据点,加一个 -a
,默认否
设置运行时的线程数量,加一个 -c xxx
,默认是 10
设置运行时的栈空间大小,加一个 -m xxx
(单位是 MB),默认是 128 MB
设置运行时的时限,加一个 '-t xxx' (单位是 ms),默认是 1000ms
设置编译时是否关闭 O2、UB sanitizer:什么都不加是都开(会拥有两到三倍常数),-f
是只开 O2,-ff
是都不开(打树剖平衡树啥的这样编译的时间会快点,否则一个编译四五秒等的太急了)
设置非 AC 时立即停止运行,加一个 -t
,默认不停止
设置不编译,直接用上次编译好的程序,加一个 -n
,默认关闭此选项
对拍就这样
等下补上来本地多组测试点评测和数据生成器