#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 等。
反面教材
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
正面教材
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];// ....