ACM/XCPC对拍(Linux/Windows)

前言

心血来潮,整理一手c++对拍,分别是Linux下的脚本对拍和windows下的代码对拍

Windows对拍

windows下的对拍总共三个文件分别是正解(ok.cpp)错解(bad.cpp)和对拍生成数据的文件,对拍的时候只需要运行生成数据文件(beat.cpp)即可。下面给出三个文件示例代码
正解示例代码:ok.cpp

#include <bits/stdc++.h>
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int a, b;
    std::cin >> a >> b;
    std::cout << a + b << "\n";
}

错解示例代码:bad.cpp

#include <bits/stdc++.h>
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int a, b;
    std::cin >> a >> b;
    if (rng() % 10 >= 1) {
        std::cout << a + b << '\n';
    } else {
        std::cout << a + b + 1 << '\n';
    }
}

对拍示例代码:beat.cpp
.\beat是我的对拍存放的目录,可以自行更改,如果存放在你当前开的文件夹下面,代码中的.\beat\都可以不打,默认运行当前文件。

#include <bits/stdc++.h>
int main() {
    #define COMPLILE // 是否重新编译,不需要重新编译注释掉就行了
    #ifdef COMPLILE
        system("g++ -std=c++2a .\\beat\\ok.cpp -o .\\beat\\ok.exe -O3");
        system("g++ -std=c++2a .\\beat\\bad.cpp -o .\\beat\\bad.exe -O3");
    #endif
    for (int i = 0; i < 1000000; ++i) {
        std::cout << "Testcase: " << i << '\n';
        {
            std::ofstream cout("in.txt");
            std::uniform_int_distribution<int> dist(2, 1e9);
            std::mt19937 mt(std::chrono::steady_clock::now().time_since_epoch().count());
            #define rng() dist(mt)
            // std::random_device rd;
            // #define rng() dist(rd)

            cout << rng() % 10 << ' ' << rng() % 10 << "\n";

            cout.close();
        }



        system(".\\beat\\ok.exe < .\\beat\\in.txt > .\\beat\\ok.txt");
        system(".\\beat\\bad.exe < .\\beat\\in.txt > .\\beat\\bad.txt");
        // getchar(); // 单步回车比较
        // system("fc .\\beat\\ok.txt .\\beat\\bad.txt")
        if (system("fc .\\beat\\ok.txt .\\beat\\bad.txt")) { // 自动比较 
            /* 是否将正确结果和错误结果输出在输入文件,方便查看,自行选择是否需要
            system("echo ok: >> .\\beat\\in.txt");
            system("type .\\beat\\ok.txt >> .\\beat\\in.txt");
            system("echo bad: >> .\\beat\\in.txt");
            system("type .\\beat\\bad.txt >> .\\beat\\in.txt");
            */
            puts("WA!!!");
            break;
        }
    }
}

最后放一个我的文件目录结构

Linux对拍

我的Linux对拍跟windows有点区别,我是使用脚本对拍同时也是用脚本运行文件(当然也可以跟windows一样使用文件对拍)。
下面先放一些linux常用指令, 方便后面对拍代码的理解

clear
功能:清理屏幕,Ctrl+l快捷键具有相同的功能

pwd
功能:显示当前所在的工作目录

cd <path>
功能:改变当前的工作目录

ls [参数] [路径]
功能:显示指定路径下有哪些文件,如果没有路径参数则显示当前工作目录下有哪些文件
 
man [n] <key> 查看系统的帮助手册
	
time <可执行文件> 
功能:执行一个程序,并记录该程序的执行时间
	real	0m0.015s 程序执行的总用时
	user	0m0.013s 用户态执行的时间
	sys		0m0.000s 内核态执行的时间
	real = user + sys + 用户态内存态切换消耗的时间

diff [选项] <可执行文件> <可执行文件>
逐行比较<各文件>。

touch <filename>
功能:创建新文件

rm <filename> 
功能:删除文件,删除的文件不经过回收站,而是直接从文件系统中删除,很难恢复,删除时要慎重

cp <src> <<path>/[filename]> 
功能:复制文件,可以在复制过程中给目标文件取个新名字

mv <src> <<path>/[filename]>
功能:移动文件,也可以在移动过程中给目标文件取个新名字,并且它还具有重命名的功能

cat <filename>
功能:查看文件内容,它会把文件的所有内容都输出到屏幕上,但不适合用来查看内容比较多的文件

mkdir <dirname>
功能:创建目录
	-p 可以创建多级目录

rm -rf <dirname>
功能:强制删除非空目录
	
cp -r <srcdir> <<path/>[dirname]> 
功能:直接使用cp复制目录默认会忽略,需要加上r参数

mv 在移动目录和重命名目录时,不需要加任何参数,直接使用即可

chmod
功能:修改文件权限
	用法1:chmod mmm <filename>
            每个 m = 4r 2w 1x 都由组成一共有8种情况
            0 ---   1 --x   2 -w-   3 -wx 
            4 r--   5 r-x   6 rw-   7 rwx
            第一m 对应前三个字符 文件的主人的权限(属主)
            第二m 对应中间三个字符 与文件的主人同一级用户的权限(属组)
            第三m 对应末尾三个字符 其它用户的权限
	用法二:chmod +/- r、w、x <filename> 所有用户一起增加或减少某一项权限。
    注意:目录文件需要执行权限才能进入,常用的两种权限:644普通文件,755目录文件

timeout [选项] 停留时间 命令 [参数]...
功能:运行指定命令,在指定的停留时间后若该命令仍在运行则将其中止。

code <filename>
功能:使用vsc打开文件,没有就创建

vi/vim <filename>
功能:使用vim打开文件,没有就创建

wc [参数] [路径]
功能:(字数统计)命令打印文件中的行数、字数和字节数。
	-l 只打印行数
	-w 只打印字数
	-c 仅打印字节数

下面介绍以下如何对linux的终端进行配置自定义命令,使得打的代码更短 。
1、在终端输入 sudo vim ~/.bashrc,按i进入插入模式,从最底行开始编辑
2、ESC进入命令模式,按:键输入wq保存并退出
​3、重新加载配置文件:source ~/.bashrc 实现精简命令提示信息的效果
下面是我的部分配置

alias g++='g++ -std=c++17'

mk() { #创建目录并进入
	mkdir $1 && cd $1
}
thsh() { #创建脚本并赋予权限,然后vsc打开
	touch $1 && code $1 && echo "#!/bin/bash" > $1 && chmod u+x *.sh
}
thcpp() { #创建cpp文件,并用vsc打开
	touch $1 && \
		echo "#include <bits/stdc++.h>" > $1 &&
		echo "int main() {" >> $1 &&
		echo "    std::cin.tie(nullptr)->sync_with_stdio(false);" >> $1 &&
		echo "}" >> $1  &&
		code $1
}
alias cu='cd ..' # 返回上级目录

最后是我的linux对拍脚本(如果没有权限则使用chmod -x beat.sh设置运行权限)
beat.sh 对拍文件
-std=c++2a-O3可以根据自己选择是否添加

#!/bin/bash
g++ -std=c++2a ok.cpp -o ok -O3
g++ -std=c++2a sol.cpp -o bad
g++ -std=c++2a gen.cpp -o gen
for tt in $(seq 1 100)
do
    echo ===== Testcase $tt =====
    ./gen > in
    timeout 1 ./ok < in > okout
    timeout 1 ./bad < in > badout
    diff -q -b okout badout
    if [[ $? != 0 ]]
    then
        echo \> ok 
        echo $(cat <okout)
        echo \> bad
        echo $(cat <badout)
        echo \> Example
        cat in
        ./ok < in > ans$(find in* | wc -l) # 创建错误样例正确结果文件
        cp in in$(find in* | wc -l) #将错误数据设置为最后一个.in
        break
    fi
done

gen.cpp 生成数据文件

#include <bits/stdc++.h>
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
using std::cout;
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    cout << rng() % 10 << ' ' << rng() % 10 << '\n';
}

run.sh 运行文件
我这里运行的是a.cpp文件,linux如果不指定则会默认将编译结果储存在a.out 里面。

#!/bin/bash
g++ -std=c++2a sol.cpp

#运行所有测试点
for file in ./in*
do
    diff -q -b <(timeout 1 ./a.out < "$file") ans${file#*in}
    if [[ $? != 0 ]]
    then
        echo ===== Error ${file#*in} =====
        echo Test: 
        cat $file
        echo ok: 
        cat < ans${file#*in} 
        echo bad: 
        timeout 1 ./a.out < "$file" # 同行输出
    else
        echo ===== AC ${file#*in} =====
    fi
done

# 对拍错误样例
# echo ===== Test Case Error =====
# time ./a.out < in

# 手搓
# echo ===== My ===== 
# timeout 1 ./a.out

# 运行所有样例 并输出到对应的ans文件
# for file in ./in*
# do
#     echo "runing on ${file#*in}"
#     ./a.out < "$file" > ans${file#*in}
# done

最后的ac.cppbad.cpp和Windows一样就不贴了。然后依旧是附一张我的linux对拍目录结构

Makefile执行cpp

CXX := g++ # 编译器选项
CXXFLAGS = -std=c++2a # 编译器参数
OBJS = sol.cpp 
TARGET := sol # 此处添加最后生成的可执行文件名

all:$(TARGET)
	@./$^ <in1 

$(TARGET):$(OBJS)
	@(CXX) $(CXXFLAGS) -o $@ $^
	@rm -rf *.o

数据生成

最后放一下图和树的数据制造代码

{ // 随机生成图
    int n = rng();
    std::vector<std::pair<int, int>> h;
    std::vector<std::vector<int>> g(n + 1, std::vector<int>(n + 1));
    for (int i = 2; i <= n; ++i) {
        int x = rng() % (i - 1) + 1;
        g[x][i] = 1;
        h.emplace_back(x, i);
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = i + 1; j <= n; ++j) if (!g[i][j] && rng() % 2) {
            g[i][j] = 1;
            h.emplace_back(i, j);
        }
    }
    cout << n << ' ' << size(h) << "\n";
    for (auto [a, b] : h) {
        cout << a << ' ' << b << ' ' << rng() << "\n";
    }
}
{ // 随机生成树
    int n = rng();
    cout << n << '\n';
    for (int i = 2; i <= n; ++i) {
        cout << rng() % (i - 1) + 1 << ' ' << i << "\n";
    }
}
posted @ 2024-05-22 11:25  sleeeeeping  阅读(256)  评论(0编辑  收藏  举报