对拍Bete

前言

那天LZY需要对拍,题目样例给的是一个图,LZY发现自己的对拍程序并不能生成图。
这时候msjing站出来说:

我有随机生成图的程序。

这么好?
于是我直接拿来用。
用着用着不对劲。

  • 你这图生成的什么玩意?
  • msjing不知道

靠别人不如靠自己,在我坚持了3.1415926个小时后,终于写成了一个较为强大的随机样例生成器,大部分常用生成器均已打包进了函数里。

完整对拍程序

下载

注意:这个有亿点锅。

版本(胡扯) 系统 语言 下载
1.2.0(测试) Windows C++14及以上 链接
1.1.5(测试) Windows C++14及以上 链接
1.1.4(正式) Windows C++14及以上 链接
1.1.3(正式) Windows C++14及以上 链接
1.1.2(测试) Windows C++14 链接
1.1.1(正式) Windows C++14 链接

使用教程[以1.1.4(正式)为准]

  1. 将文件解压到桌面(只有解压到桌面快捷方式才起作用!)。
  2. 打开解压后的「对拍」文件夹。
  3. 分别打开「待测程序」、「正确程序」将待测代码与暴力/正确代码放进去,要保留freopen文件头(要是没了去文章后面Copy)。
  4. 打开「随机样例生成器」按下面的这个教程造样例。
  5. 准备完成后运行「开始」,按下回车即可开始对拍。

若有 Special Judge 的需要,请打开「Face-off」文件夹中的「Checker.cpp」文件,返回值与报错信息的对应关系均已注释,其中「corr」为从正确程序的输出中输入,「test」为从待测程序的输出中输入。

随机样例生成器

随机样例生成器
#include<bits/stdc++.h>
#include<tuple>
#include<vector>
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define dbl double
using namespace std;
mt19937_64 num(time(0));

template<typename T>inline T INT(T l,T r,char out=0){T x=num()%(r-l+1)+l;if(out)cout<<x<<out;return x;}
template<typename T>inline vector<T> INT(int len,T l,T r,char out=0,char out2=0){vector<T> v(len);for(int i=0;i<len;++i) v[i]=INT(l,r,out);if(out2) cout<<out2;return v;}

template<typename T>inline T FLO(T l,T r,char out=0,int pos=3){
	uniform_real_distribution<long double> flo(l,r);
	T x=flo(num);
	if(out) cout<<fixed<<setprecision(pos)<<x<<out;
	return x;
}
template<typename T>inline vector<T> FLO(int len,T l,T r,char out=0,char out2=0,int pos=3){
	vector<T> v(len);
	for(int i=0;i<len;++i) v[i]=FLO(l,r,out,pos);
	if(out2) cout<<out2;
	return v;
}

inline char CH(char l,char r,char out=0){char x=num()%(r-l+1)+l;if(out)cout<<x<<out;return x;}
inline char CH(string s,char out=0){char x=s[INT(0,(int)s.size()-1)];if(out)cout<<x<<out;return x;}
inline string STR(int len,char l,char r,char out=0,char out2=0){string s;for(int i=1;i<=len;++i) s+=CH(l,r,out);if(out2) cout<<out2;return s;}

template<typename T> inline T Pick(initializer_list<T> s,char out=0){
	T x=*(s.begin()+num()%s.size());
	if(out) cout<<x<<out;
	return x;
}
template<typename T>inline vector<T> Pick(int len,initializer_list<T> s,char out=0,char out2=0){
	vector<T> v(len);
	for(int i=0;i<len;++i) v[i]=Pick(s,out);
	if(out2) cout<<out2;
	return v;
}

pair<int,int> ITV(int l,int r,bool out=0){
	int x=INT(l,r),y=INT(l,r);
	if(x>y) swap(x,y);
	if(out) cout<<x<<' '<<y;
	return {x,y};
}

auto Graph(int point,int edge,int weightl=0,int weightr=0,vector<string> t={}){
	map<string,bool> type;
	for(string s:t) type[s]=1;
	bool repeat=type["repeat"],directed=type["directed"],
	forest=type["forest"],self=type["self"],
	heap=type["max-heap"]|type["min-heap"]|type["heap"];
	vector<tuple<int,int,int> > g;
	map<pair<int,int>,bool> vis;
	vector<int> tmp;
	if(type["max-heap"]) for(int i=point;i>=1;--i) tmp.emplace_back(i);
    else for(int i=1;i<=point;++i) tmp.emplace_back(i);
	if(!heap) shuffle(tmp.begin(),tmp.end(),num);
	if(!forest)
    for(int i=1,x,y;i<(int)tmp.size();++i){
        x=tmp[INT(0,i-1)],y=tmp[i];
        if(!repeat){
			if(directed)vis[{x,y}]=1;
			else vis[{x,y}]=vis[{y,x}]=1;
		}
		g.emplace_back((tuple<int,int,int>){x,y,INT(weightl,weightr)});
    }
	if(!forest) edge-=point-1;
	if(!type["tree"])
	while(edge>0){
		int x=INT(1,point),y=INT(1,point);
		while(vis[{x,y}]||(x==y&&!self)) x=INT(1,point),y=INT(1,point);
		if(!repeat){
			if(directed)vis[{x,y}]=1;
			else vis[{x,y}]=vis[{y,x}]=1;
		}
		g.emplace_back((tuple<int,int,int>){x,y,INT(weightl,weightr)});
		edge--;
	}
	if(type["value"]) for(auto x:g)cout<<get<0>(x)<<' '<<get<1>(x)<<' '<<get<2>(x)<<'\n';
	else if(type["out"]) for(auto x:g)cout<<get<0>(x)<<' '<<get<1>(x)<<'\n';
	return g;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	freopen("example.in","w",stdout);
	
	return 0;
}

使用教程[以1.1.4(正式)为准]

随机生成整数INT()

使用INT()函数可以随机生成整数,返回值类型与传入参数 \(l,r\) 有关。

函数参数:

\[INT(l,r,out) \]

随机生成整数,返回一个整数。

  • \(l,r\) (必要参数)表示生成随机数的范围 \([l,r]\)
  • \(out\) (非必要参数)表示是否输出及输出类型,默认为 \(0\),即不输出,若传入「空格」则表示将生成的随机数输出并输出后输出「空格」。

\[INT(len,l,r,out,out2) \]

生成随机整数数列,返回一个vector

  • \(len\) 表示生成随机数列的长度。
  • \(l,r\) 同上。
  • \(out\) 表示数列中每一个数的输出类型,默认为 \(0\)
  • \(out2\) 表示整个序列的输出类型,默认为 \(0\)

注意:

为避免传参混乱的问题,请不要写类似 \(INT(int,int,int)\) 的传参。

随机生成浮点数FLO()

使用FLO()函数可以生成随机浮点数,返回值类型与 \(l,r\) 有关。

函数参数:

\[FLO(l,r,out,pos) \]

随机生成浮点数,返回一个浮点数。

  • \(l,r,out\) 同上。
  • \(pos\) 表示输出时小数的位数。

\[FLO(len,l,r,out,out2,pos) \]

生成随机浮点数数列,返回一个vector

  • \(len,l,r,out,out2,pos\) 同上。

随机生成字符(串)CH()/STR()

使用CH()函数可以生成一个字符,STR()可以生成一个字符串。

函数传参:

\[CH(l,r,out) \]

生成一个字符,返回char

  • \(l,r,out\) 同上。

\[STR(len,l,r,out,out2) \]

生成一个字符串,返回一个string

  • \(len,l,r,out,out2\) 同上。

随机选择Pick()

使用Pick()可以在一个有限集合内选择元素。

函数传参:

\[Pick(s,out) \]

在集合中选择一个元素,返回该元素。

  • \(s\) 传入的集合,集合内元素使用花括号{}包裹,逗号分割。
  • \(out\) 同上。

\[Pick(len,s,out,out2) \]

多次随机选择元素组成一个元素列,返回一个vector

  • \(len,s,out,out2\) 同上。

随机生成区间ITV()

使用ITV(),随机生成一个区间。

函数传参:

\[ITV(l,r,out) \]

生成 \([l,r]\) 的一个子区间,返回一个pair<int,int>

  • \(l,r\) 区间范围。
  • \(out\) 同上。

随机生成图Graph()

使用Graph函数可以快速生成一个图。
完整参数如下。
Graph(int point,int edge,int weightl=0,int weightr=0,vector<string> t={})

  • point 表示节点数。
  • edge 表示边数。
  • weightlweightr 表示权值的范围。

最后的vector<string> t={}表示指令。

  • tree 指令可以让函数强制生成一颗树,只在乎节点数,不在乎边数。
  • forest 指令允许函数生成的图不连通,即森林。
  • directed 指令会使函数认为\(x \rightarrow y\)\(y \rightarrow x\)不是一条边,即生成有向图。
  • out 指令可以让函数直接将这颗树输出。
  • value 指令可以让函数输出时额外输出边权。
  • repate 指令可以允许函数生成重边。
  • self 指令可以允许函数生成自闭环。
  • heap 类命令可以让函数生成的树满足堆性质,默认为小根堆,min-heapmax-heap分别对应小根堆、大根堆。
auto g=Graph(10,10); //生成10个节点10条边的基环树。
auto g=Graph(10,100,1,1000,{"value","repeat","forest"});
//生成一个10个节点100条边、可重边、权值在 1~1000 之间的带边权的无向图,并带权输出,同时将图返回给变量 g。其中允许生成的图不连通。

注意

  • 若你不传任何指令,函数默认生成一个不重边、无自闭环的无向联通图,返回值类型为vector<tuple<int,int,int> >tuple中的三个元表示 边与权值。

  • 此生成随机图函数为不完整版,生成速度较慢,请不要使用它生成规模 \(1e5\) 以上的树,若要生成 \(1e6\) 规模的图请加上 forestrepeat 指令。

  • 若没有forest指令,函数会先生成一颗树,再在树的基础上加边,也就是说如果传入的边数小于节点数减一,那么函数将返回一颗树。

  • 若没有repeat指令,传入的边数远远大于节点数的话,函数会四掉的。

  • 仍要注意的是无论你是否选择输出,函数都会将图返回。

答案比较器

答案比较器
#include<bits/stdc++.h>
using namespace std;
ifstream corr,test;

/*
return 0: Accepted
return 1: Wrong answer: Objective wrong answer
return 2: Wrong answer: Output longer than answer
return 3: Wrong answer: Output shorter than answer
return -1: System error: can't find corrent.out
return -2: System error: can't find tested.out
else System error: Unknown Error 
*/ 

int Judge(){
	//Write Special Judge here
	string s1,s2;
	bool f=1;
	while(corr>>s1&&test>>s2){
		if(s1!=s2) f=0;s1="",s2="";
	}
	corr>>s1;test>>s2;
	if(s1==""&&s2!="") return 2;
	if(s2==""&&s1!="") return 3;
	if(f) return 0;
	else return 1;
} 

signed main(){
	ios::sync_with_stdio(0);
	
	corr.tie(0),test.tie(0);
	corr.open("corrent.out");
	test.open("tested.out");
	if(!corr.is_open()) return -1;
	if(!test.is_open()) return -2;
	
	return Judge();
}

对拍启动器

对拍启动器
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;

void color(int c) {
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, c);
}

bool flag_check=1;

void sleep(){
	char c=cin.get();
	string s;
	while(c!='\n') s+=c,c=cin.get();
	if(s=="/close check") flag_check=0;
}

bool check(){
	ifstream G,C,T;
	G.open("Generator.exe",ios::binary);
	C.open("Correct.exe",ios::binary);
	T.open("tested.exe",ios::binary);
	bool f=0;
	if(!G.is_open()||!C.is_open()||!T.is_open()) f=1;
	if(f){
		color(12);
		if(!G.is_open()) cerr<<"Warning: Generator hasn't been compiled.\n";
		if(!C.is_open()) cerr<<"Warning: Correct hasn't been compiled.\n";
		if(!T.is_open()) cerr<<"Warning: Tested hasn't been compiled.\n";
		color(7);
		sleep();
		return 1;
	}
	G.close(),C.close(),T.close();
	return 0;
}

int main(){
	int tot=0;
	clock_t st,ed;
	sleep(); 
	while(1){
		if(flag_check) if(check()) continue;
		cerr<<"test "<<++tot<<": \t";
		system("Generator.exe");

		system("Correct.exe");
		st=clock();
		int tes=system("Tested.exe");
		ed=clock();

		double tim=(double)ed-st;
		if(tes){
			color(6);
			cerr<<"Runtime Error: Your program returned "<<tes<<".\t\t";
			cerr<<tim<<" ms\n"; 
			color(7);
			sleep();
			continue;
		}

		int res=system("Checker.exe");

		switch(res){
			case -2:
				color(13),cerr<<"System Error: Can't find tested.out.\t\t";break;
			case -1:
				color(13),cerr<<"System Error: Can't find corrent.out.\t\t";break;
			case 0:
				color(10),cerr<<"Accepted.\t\t";break;
			case 1:
				color(4),cerr<<"Wrong Answer: Objective wrong answer.\t\t";break;
			case 2:
				color(4),cerr<<"Wrong Answer: Output longer than answer.\t\t";break;
			case 3:
				color(4),cerr<<"Wrong Answer: Output shorter than answer.\t\t";break;
			default:
				color(13),cerr<<"System Error: Unknow Error.\t\t";break;
		}
		cerr<<tim<<" ms\n"; 
		color(7);
		if(res) sleep();
	}
	return 0;
}

正确程序头

	freopen("example.in","r",stdin);
	freopen("corrent.out","w",stdout);

待测程序头

	freopen("example.in","r",stdin);
	freopen("tested.out","w",stdout);

更新日志

  • 2026/7/25 16:05 略微降低了生成树的空间开销。
  • 2026/7/25 16:39 Graph函数更新了heapmin-heapmax-heap命令。更新了排版。
  • 2026/7/25 20:51 加入了答案比较器、对拍启动器、正确程序头与待测程序头,至此,完整对拍程序已经完工。
  • 2026/7/26 10:39 更新1.1测试版,加入计时功能。
  • 2026/7/26 10:48 将此文章同步与洛谷。
  • 2026/7/26 19:14 更新1.1.1正式版优化了Special Judge的书写难度,新增了REUKE报错。
  • 2026/7/26 20:19 修复了一些BUG。由于不同编译器对中文注释的处理不同,将中文注释改为了英文注释,并优化了文件的大小。
  • 2026/7/27~2026/7/31 陆续发布了1.1.2(测试)、1.1.3(正式)、1.1.4(正式)

更新内容:

1.1.4(正式):

  • 加入了Warning报错。
  • 正式加入报错信息说明。
  • 优化了Special Judge的书写环境。
  • 优化了文章排版。

1.1.3(正式):

  • 修复了1.1.2模板函数的锅。
  • 更改了「随机样例生成器」的生成随机数函数。
  • 加入了随机浮点数的生成。
  • 从原函数分离出Pick()函数。

1.1.2(测试):

  • 尝试加入报错信息说明。
  • 使用模板函数合并相似函数(也出了巨大的锅)。

1.1.1(正式):

  • 优化了Special Judge书写。
  • 新增REUKE报错。

1.1(测试):

  • 加入计时功能。

1.0 :

  • 完整对拍程序,可以完成对拍。
posted @ 2026-07-25 08:22  LZYXT  阅读(35)  评论(7)    收藏  举报