前言
最近我同學有在架設我們學校的線上評測系統,而一開始我們的系統非常不穩定,因此,他想到可以寫離線的程式答題系統緩解我們上課的需求。而我看到這個系統,我覺得這是一個不錯的想法,並依此加以改良,新增 TLE 判定和超時時會強制停止,並且可以跨平台使用。
使用(解題者)
一題會是一個資料夾,裡面會有隱藏檔案和非隱藏檔案。
檔案名稱 | 大致內容 | 是否隱藏 |
---|---|---|
AC | ascii art 的 AC 字樣 | True |
WA | ascii art 的 WA 字樣 | True |
TLE | ascii art 的 TLE 字樣 | True |
main.h | 主要程式 | 原則上 True |
Run.cpp | 執行程式 | False |
Solve.h | 使用者的程式 | False |
Description | 題目敘述 | False |
README.txt | 說明使用方法(如下) | False |
README.txt 內文
以下說明檔案功能及用法
- 編譯並執行 Run.cpp 可以獲得評測結果
- 程式碼請寫在 Solve.h 裡面
- 題目敘述在 Description 裡面,一般應該是 .txt,.md 或 .pdf
- 標準輸入輸出用 cin 和 cout 就可以了
以下說明答題結果
- AC 作答正確
- WA 輸出結果錯誤,或不符合題目要求
- TLE 執行時間過長
- 程式跳掉 一般來說都是 RE(runtime error)
以下規定請勿違反
- 擅自更改除 Solve 以外的檔案
貼心小提醒
由於 Solve 函式會被重複使用,因此變數就算開在全域也要記得初始化
截圖
使用(出題者)
資料夾中還含有一個隱藏資料夾 TestCase,裡面存放有測試資料。裡面的檔案如下
檔案名稱 | 大致內容 |
---|---|
Gen.cpp | 預設的測試資料生成程式 |
GraphGen.h | 圖論測資生成程式 ( 使用介紹 ) |
log.txt | 測試資料們的基本資料 |
OJ | 生成可以上傳到 UOJ 系統的測試資料 |
Gen.cpp
介紹各個函式的功能
函式 | 功能 |
---|---|
RandomNumber(long long a,long long b) | 生成一個隨機數 ( 範圍[a,b] ) |
RandomNumber(long long n) | 生成一個隨機數 ( 範圍[1,n] ) |
solve() | 共用解答函式 ( 有需要的話 ) |
SubTesk1(int a) | 生成第 a 筆測資 |
如果覺得 RandomNumber 太長,GraphGen.h 裡面有相同功能的函式 Rand
其他都不是非常重要,主要是可以同時支援 UOJ 格式的測資。以下是 a+b problem 的範例。
code
#include<bits/stdc++.h>
#include "GraphGen.h"
using namespace std;
unsigned seed=chrono::steady_clock::now().time_since_epoch().count();
mt19937_64 rng=mt19937_64(seed);
long long RandomNumber(long long a,long long b){
uniform_int_distribution<long long> dis(a,b);
return dis(rng);
}
long long RandomNumber(long long n){
return RandomNumber(1,n);
}
void solve(){
;
}
void SubTesk1(int a){
string fileName=to_string(a);
ofstream ques(fileName+".in"),ans(fileName+".out");
int aa=RandomNumber(10000),b=RandomNumber(10000);
ques<<aa<<" "<<b<<"\n";
ans<<aa+b<<"\n";
cout<<a<<endl;
}
void UOJ(int a){
string fileName="./OJ\\a"+to_string(a);
ofstream ques(fileName+".in"),ans(fileName+".out");
int aa=RandomNumber(10000),b=RandomNumber(10000);
ques<<aa<<" "<<b<<"\n";
ans<<aa+b<<"\n";
cout<<a<<endl;
}
void ExTest(int a){
string fileName="./OJ\\ex_a"+to_string(a);
ofstream ques(fileName+".in"),ans(fileName+".out");
int aa=RandomNumber(10000),b=RandomNumber(10000);
ques<<aa<<" "<<b<<"\n";
ans<<aa+b<<"\n";
cout<<a<<endl;
}
#define REP(i,a,b) for(int i=(a);i<(b);++i)
int main(){
ios::sync_with_stdio(0);cin.tie(0);
clock_t startTime=clock();
// 測資生成函式使用都放在這裡面
REP(i,1,5+1){
SubTesk1(i);
}
REP(i,1,10+1){
UOJ(i);
}
REP(i,1,10+1){
ExTest(i);
}
// End Of Processes
clock_t endTime=clock();
cout<<double(endTime-startTime)/1000<<"\n";
}
log.txt
只有兩行,第一行是測試資料數目,第二行是執行時間限制,以毫秒為單位。
TestCases: 5
TimeLimit(ms): 50
主程式
函式 | 功能 |
---|---|
RunCode(int timeLimit) | 執行使用者程式(會計算時間,盡量減少多餘程式碼) |
RunTestCase(int testCase,int timeLimit) | 完成執行第 testCase 筆測試資料所需的前置條件 |
Judge(int testCase) | 評判解答正確性 |
RunSolution() | 執行所有測試並輸出結果 |
code
#ifndef MAIN_H
#define MAIN_H
#include<bits/stdc++.h>
#include<conio.h>
#include "Solve.h"
using namespace std;
bool isfinish=false;
vector<int> costTime;
// TLE 例外物件
class TimeLimitExceeded : public exception{
public :
TimeLimitExceeded() : exception() {}
};
void RunCode(int timeLimit){
isfinish=false;
clock_t start=clock();
Solve();
isfinish=true;
clock_t end=clock();
if(end-start>timeLimit){
throw TimeLimitExceeded();
return;
}
costTime.emplace_back(end-start);
}
bool RunTestCase(int testCase,int timeLimit){
string fileNum=to_string(testCase);
string inputFile="./TestCase\\";
inputFile+=fileNum;
inputFile+=".in";
string userOutput="./TestCase\\sol";
userOutput+=fileNum;
userOutput+=".out";
freopen(inputFile.c_str(),"r",stdin);
freopen(userOutput.c_str(),"w",stdout);
thread run{RunCode,timeLimit};
chrono::milliseconds s(timeLimit+20);
this_thread::sleep_for(s);
if(!isfinish){
run.detach();
throw TimeLimitExceeded();
return false;
}else{
run.join();
return true;
}
}
bool Judge(int testCase){
ifstream userOutput("./TestCase\\sol"+to_string(testCase)+".out");
ifstream question("./TestCase\\"+to_string(testCase)+".in");
ifstream answer("./TestCase\\"+to_string(testCase)+".out");
string tp,userAns,systemAns;
while(userOutput>>tp){
userAns+=tp;
if(userAns.size()>10000000){
return false;
}
}
while(answer>>tp){
systemAns+=tp;
}
cerr<<" ";
return systemAns==userAns;
}
void RunSolution(){
ifstream log("./TestCase\\log.txt");
string recycle;
int testCases,timeLimit;
log>>recycle>>testCases;
log>>recycle>>timeLimit;
cerr<<"There're "<<testCases<<" testcases."<<"\n";
cerr<<"Running TestCase..."<<"\n";
try{
for(int i=1;i<=testCases;++i){
RunTestCase(i,timeLimit);
cerr<<i<<" ";
}
cerr<<"\n\n";
}catch(TimeLimitExceeded){
ifstream TLE("TLE");
string line;
while(getline(TLE,line)){
cerr<<line<<"\n";
}
cerr<<flush;
getch();
exit(0);
}catch(exception){
ifstream RE("RE");
string line;
while(getline(RE,line)){
cerr<<line<<"\n";
}
cerr<<flush;
getch();
exit(0);
}
bool allCorrect=true;
vector<bool> outputCorrect(testCases+1);
for(int i=1;i<=testCases;++i){
outputCorrect[i]=Judge(i);
if(!outputCorrect[i]){
allCorrect=false;
}
}
cerr<<"\n";
if(allCorrect){
ifstream AC("AC");
string line;
while(getline(AC,line)){
cerr<<line<<"\n";
}
cerr<<flush;
}else{
ifstream WA("WA");
string line;
while(getline(WA,line)){
cerr<<line<<"\n";
}
cerr<<flush;
}
string ret[2]{"WA","AC"};
cerr<<"For each testcase : "<<"\n\n";
for(int i=1;i<=testCases;++i){
cerr<<right<<setw(3)<<i<<". "<<flush;
cerr<<ret[outputCorrect[i]]<<" "<<flush;
cerr<<"Execution time : "<<right<<setw(4)<<costTime[i-1]<<" ms"<<endl;
}
cerr<<flush;
getch();
}
#endif
競賽中不會出現的 C++ 功能
多執行續 thread 函式庫
// 開新的執行續處理函式 RunCode
thread run{RunCode,timeLimit};
// 讓此執行續等待一段時間 chrono 格式
this_thread::sleep_for(chrono::milliseconds(timeLimit+20))
// 放空該執行續(與物件 run 取消綁定,但會繼續執行)
run.detach();
// 合併執行續,意味著它會等待到 run 那個執行序完成(return)後再合併
run.join();
其它題目
可以去雲端硬碟下載。這裡有一些題目。Mega