# 如何出題 ## 題目的發想 大致上有兩種出發點: 1. 把生活中常見的問題、數學考卷遇到的問題、其他OJ看到的問題當靈感 2. 先設定某個(或某些)考點,再去想題目 ## 寫題敘 - **最最最重要的部分**: :::danger ### 務必清楚表達題意 ::: 要以解題者的角度思考過題序是否會出現難以判斷或是有歧義的敘述或是狀況 ||除非你要出梗題|| - 發揮想像力 > 題敘就是越長越好 > ——38th教學 微崩 也可以請生成式AI或是其他作文大師幫忙想題序(甚至是題目) ## 輸出入格式 - 需要講清楚的部分: 1. 是單筆測資還是EOF 2. 輸入的方式(格式) 3. 幾個變數、變數的意義、型別、範圍 - 要注意的地方: 1. 數學語言記得用LaTeX語法 ([語法大全連結](https://hackmd.io/@RintarouTW/%E6%84%9A%E5%8D%83%E6%85%AE%E3%81%AE%E7%AD%86%E8%A8%98%E6%9C%AC/%2F%40RintarouTW%2FLaTeX_%25E8%25AA%259E%25E6%25B3%2595%25E7%25AD%2586%25E8%25A8%2598)) 2. 小心很多零的數字別打錯 3. 可以請AI幫你檢查敘述 ## 生測資 ### 要點 - 不能讓對的變錯的,也不能讓錯的變對的 - 題目的最大最小範圍要卡到 - worth case也要卡到 ### 模板 - 寫檔(輸出到檔案): :::spoiler template ```cpp= #include<bits/stdc++.h> using namespace std; int main() { ofstream outf("1.in"); //開檔案 if (!outf.is_open()) return 1; //output int n=100; outf << n << '\n'; outf.close(); //關檔案 } ``` ::: - 讀檔(從檔案輸入)+寫檔: :::spoiler template ```cpp= #include<bits/stdc++.h> using namespace std; int main() { ofstream outf("1.out"); //開檔案 ifstream inf("1.in"); //開檔案 if (!outf.is_open() || !inf.is_open()) return 1; //intput int n; inf >> n; //output outf << n << '\n'; outf.close(); //關檔案 inf.close(); //關檔案 } ``` ::: - 如何開.in或.out檔: 1. 從編輯器(e.g.:VS code)直接開檔案 2. 從終端機開檔案 - 隨機函式的使用教學: :::spoiler template ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; signed main() { //宣告 mt19937 rnd(chrono::system_clock::now().time_since_epoch().count()); //long long用mt19937_64 //呼叫 cout << "random: " << rnd() << '\n'; //[0,2^32-1] int k=20; //隨機取數 cout << rnd()%k << '\n'; //[0,k) /*uniform_real_distribution<> dis(0,k); //STL內建函式, 範圍[0,k) cout << (int)dis(rnd) << '\n';*/ int m=4; //設定觸發機率 if (rnd()%k<m) //有(m/k)機率觸發 { //do something } int n=5,a[]={1, 2, 3, 4, 5}; //使陣列亂序 random shuffle for (int i=0;i<n;i++) swap(a[i],a[rnd()%n]); //random_shuffle(a,a+n); //STL內建函式 } ``` ::: - <span style="color:lightgreen">**生測資的模板**</span>: p.s. 註解掉的部分是以「dp費式數列」為舉例 :::spoiler template ```cpp= //--------------------------------------打模板 /* #include<bits/stdc++.h> #define int long long #define endl '\n' using namespace std; */ //--------------------------------------結束 //----------------------------------生測資全域變數及函式 //----------------------------------結束 void generate_in(const string& file_in_string) { ofstream cout(file_in_string); //----------------------------------------------------------生測資code /* constexpr int MAXN=1e4,MAXQ=1e5; mt19937 rnd(time(0)); int n=1+rnd()%MAXN; cout << n << endl; for (int i=0;i<n;i++) cout << 1+rnd()%MAXQ+1 << ' '; */ //---------------------------------------------------------結束 //---------------------------------------------------初始化生測資全域變數 //----------------------------------------------------------結束 cout.close(); //關檔案 } //---------------------------------------------------------------------全域變數及函式宣告 /* vector<int> dp(100005,-1); constexpr int MOD=1e9+7; int f(int n) { if (dp[n]!=-1) return dp[n]; if (n==1 || n==2) return dp[n]=1; return dp[n]=(f(n-1)+f(n-2))%MOD; } */ //---------------------------------------------------------------------結束 void generate_out(const string& file_in_string,const string& file_out_string) { ifstream cin(file_in_string); //開檔案 ofstream cout(file_out_string); //開檔案 //-------------------------------------------------------------AC code /* int n; cin >> n; while (n--) { int q; cin >> q; cout << f(q) << endl; } */ //--------------------------------------------------------------結束 //------------------------------------------------------------初始化全域變數 /* fill(dp.begin(),dp.end(),-1); */ //------------------------------------------------------------結束 cin.close(); cout.close(); //關檔案 } signed main() { string outstring = ".out", instring = ".in"; //--------------------------------------------設定開始到結束檔名 int file_begin = 1, file_end = 5; //--------------------------------------------結束 string file_in_string, file_out_string, confirm; cout <<"New file:\""<< file_begin <<'~'<< file_end <<"\"\nif okay input \"1\"\n"; //確認檔案範圍 cin >> confirm; if (confirm!="1") return 0; for (int tempint = file_begin; tempint <= file_end; tempint++) { string tempstring = to_string(tempint); file_in_string = tempstring + instring; file_out_string = tempstring + outstring; generate_in(file_in_string); generate_out(file_in_string, file_out_string); } } ``` ::: 感謝 39th網管 榮恩 提供此模板的原型 - <span style="color:lightgreen">**SPJ的判定模板**</span>(適用CSDC OJ): :::spoiler template ```cpp= /*----------------------------------------------- * 通用 SPJ (Special Judge) 範本 * 原始輸入檔為 argv[1],選手輸出檔為 argv[2] * 回傳值: * 0 => AC (Accepted) * 非 0 => WA (Wrong Answer) -----------------------------------------------*/ //-----------------------------------------打模板 #include <bits/stdc++.h> #define int long long using namespace std; //------------------------------------------結束 //---------------------------------全域變數及函數 //------------------------------------------結束 signed main(int argc, char* argv[]) { // 檢查執行參數 if (argc < 3) { cerr << "Usage: spj <input_file> <user_output_file>" << endl; return 1; // 參數錯誤視為 WA } // 開啟原始輸入檔案 (argv[1]) ifstream cin(argv[1]); if (!cin.is_open()) { cerr << "Failed to open input file: " << argv[1] << endl; return 1; } // 開啟選手輸出檔案 (argv[2]) ifstream uesr_out(argv[2]); if (!uesr_out.is_open()) { cerr << "Failed to open user output file: " << argv[2] << endl; return 1; } if (!user_out) return 1; //------------------------------算出AC答案並比對 //----------------------使用user_out輸入選手答案 /*以下以計算加總作為範例程式碼 int n; cin >> n; vector<int> data(n); for (int i = 0; i < n; i++) cin >> data[i]; long long ans = accumulate(data.begin(), data.end(), 0LL); // ===== 讀取選手輸出並判斷 ===== long long user_ans; uesr_out >> user_ans; // 範例比對 if (user_ans != ans) return 1; // WA // 若程式執行到此,即視為通過 return 0; // AC */ //------------------------------------------結束 } ``` ::: ## 驗題 1. 自己要能AC 2. 其他人能AC 3. 複雜度不對或是方法錯的code要過不了 ~~(不是比賽的話可以給時間來檢驗)~~