#include"testlib.h"intmain(intargc,char*argv[]){registerTestlibCmd(argc,argv);intpans=ouf.readInt(-2000,2000,"sum of numbers");// 假定标准输出是正确的,不检查其范围// 之后我们会看到这并不合理intjans=ans.readInt();if(pans==jans)quitf(_ok,"The sum is correct.");elsequitf(_wa,"The sum is wrong: expected = %d, found = %d",jans,pans);}
// clang-format off#include"testlib.h"#include<map>#include<vector>usingnamespacestd;map<pair<int,int>,int>edges;intmain(intargc,char*argv[]){registerTestlibCmd(argc,argv);intn=inf.readInt();// 不需要 readSpace() 或 readEoln()intm=inf.readInt();// 因为不需要在 checker 中检查标准输入合法性//(有 validator)for(inti=0;i<m;i++){inta=inf.readInt();intb=inf.readInt();intw=inf.readInt();edges[make_pair(a,b)]=edges[make_pair(b,a)]=w;}ints=inf.readInt();intt=inf.readInt();// 读入标准输出intjvalue=0;vector<int>jpath;intjlen=ans.readInt();for(inti=0;i<jlen;i++){jpath.push_back(ans.readInt());}for(inti=0;i<jlen-1;i++){jvalue+=edges[make_pair(jpath[i],jpath[i+1])];}// 读入选手输出intpvalue=0;vector<int>ppath;vector<bool>used(n,false);intplen=ouf.readInt(2,n,"number of vertices");// 至少包含 s 和 t 两个点for(inti=0;i<plen;i++){intv=ouf.readInt(1,n,format("path[%d]",i+1).c_str());if(used[v-1])// 检查每条边是否只用到一次quitf(_wa,"vertex %d was used twice",v);used[v-1]=true;ppath.push_back(v);}// 检查起点终点合法性if(ppath.front()!=s)quitf(_wa,"path doesn't start in s: expected s = %d, found %d",s,ppath.front());if(ppath.back()!=t)quitf(_wa,"path doesn't finish in t: expected t = %d, found %d",t,ppath.back());// 检查相邻点间是否有边for(inti=0;i<plen-1;i++){if(edges.find(make_pair(ppath[i],ppath[i+1]))==edges.end())quitf(_wa,"there is no edge (%d, %d) in the graph",ppath[i],ppath[i+1]);pvalue+=edges[make_pair(ppath[i],ppath[i+1])];}if(jvalue!=pvalue)quitf(_wa,"jury has answer %d, participant has answer %d",jvalue,pvalue);elsequitf(_ok,"answer = %d",pvalue);}
// clang-format off#include"testlib.h"#include<map>#include<vector>usingnamespacestd;map<pair<int,int>,int>edges;intn,m,s,t;// 这个函数接受一个流,从其中读入// 检查路径的合法性并返回路径长度// 当 stream 为 ans 时,所有 stream.quitf(_wa, ...)// 和失败的 readXxx() 均会返回 _fail 而非 _wa// 也就是说,如果输出非法,对于选手输出流它将返回 _wa,// 对于标准输出流它将返回 _failintreadAns(InStream&stream){// 读入输出intvalue=0;vector<int>path;vector<bool>used(n,false);intlen=stream.readInt(2,n,"number of vertices");for(inti=0;i<len;i++){intv=stream.readInt(1,n,format("path[%d]",i+1).c_str());if(used[v-1]){stream.quitf(_wa,"vertex %d was used twice",v);}used[v-1]=true;path.push_back(v);}if(path.front()!=s)stream.quitf(_wa,"path doesn't start in s: expected s = %d, found %d",s,path.front());if(path.back()!=t)stream.quitf(_wa,"path doesn't finish in t: expected t = %d, found %d",t,path.back());for(inti=0;i<len-1;i++){if(edges.find(make_pair(path[i],path[i+1]))==edges.end())stream.quitf(_wa,"there is no edge (%d, %d) in the graph",path[i],path[i+1]);value+=edges[make_pair(path[i],path[i+1])];}returnvalue;}intmain(intargc,char*argv[]){registerTestlibCmd(argc,argv);n=inf.readInt();m=inf.readInt();for(inti=0;i<m;i++){inta=inf.readInt();intb=inf.readInt();intw=inf.readInt();edges[make_pair(a,b)]=edges[make_pair(b,a)]=w;}ints=inf.readInt();intt=inf.readInt();intjans=readAns(ans);intpans=readAns(ouf);if(jans>pans)quitf(_wa,"jury has the better answer: jans = %d, pans = %d\n",jans,pans);elseif(jans==pans)quitf(_ok,"answer = %d\n",pans);else// (jans < pans)quitf(_fail,":( participant has the better answer: jans = %d, pans = %d\n",jans,pans);}
注意到这种写法我们同时也检查了标准输出是否合法,这样写 checker 让程序更短,且易于理解和 debug。此种写法也适用于输出 YES(并输出方案什么的),或 NO 的题目。
stream.ensuref(!used[v-1],"vertex %d was used twice",v);
Warning
请在 readAns 中避免调用 全局 函数 ::ensure/ensuref(),这会导致在某些应判为 WA 的选手输出下返回 _fail,产生错误。
建议与常见错误
编写 readAns 函数,它真的可以让你的 checker 变得很棒。
读入选手输出时永远限定好范围,如果某些变量忘记了限定且被用于某些参数,你的 checker 可能会判定错误或 RE 等。
反面教材
C++
1 2 3 4 5 6 7 8 910
// ....intk=ouf.readInt();vector<int>lst;for(inti=0;i<k;i++)// k = 0 和 k = -5 在这里作用相同(不会进入循环体)lst.push_back(ouf.readInt());// 但是我们并不想接受一个长度为 -5 的 list,不是吗?// ....intpos=ouf.readInt();intx=A[pos];// 可能会有人输出 -42, 2147483456 或其他一些非法数字导致 checker RE
正面教材
C++
12345678
// ....intk=ouf.readInt(0,n);// 长度不合法会立刻判 WA 而不会继续 check 导致 REvector<int>lst;for(inti=0;i<k;i++)lst.push_back(ouf.readInt());// ....intpos=ouf.readInt(0,(int)A.size()-1);// 防止 out of rangeintx=A[pos];// ....