)
簡介本資源是《洛谷深入淺出程序設(shè)計競賽基礎(chǔ)篇》配套代碼與學(xué)習材料合集面向算法入門者、ACM/ICPC初學(xué)者及OI訓(xùn)練營學(xué)員旨在系統(tǒng)夯實程序設(shè)計競賽核心基礎(chǔ)能力。壓縮包共91個文件主體為87個C源碼文件覆蓋枚舉、模擬、貪心、簡單DP等基礎(chǔ)算法題解輔以README.md說明文檔、LICENSE開源協(xié)議、.gitignore配置及簡要使用說明txt整體僅39KB輕量易讀目錄按章節(jié)組織Part1/Chapter1–5等結(jié)構(gòu)清晰便于循序漸進學(xué)習。已有944人下載學(xué)習內(nèi)容源自洛谷經(jīng)典教材實踐延伸每份代碼均含注釋與典型測試用例可直接編譯運行幫助讀者理解題意建模、調(diào)試邏輯漏洞并掌握標準編碼規(guī)范。1. 這不是一本普通編程書它是一套可解壓、可編譯、可調(diào)試的競賽訓(xùn)練閉環(huán)系統(tǒng)你下載了一個叫《洛谷深入淺出程序設(shè)計競賽基礎(chǔ)篇》.zip 的文件雙擊解壓后看到的不只是 PDF 和 Markdown——里面混著CMakeLists.txt、test/目錄下帶.in/.out的測試用例、src/里按章節(jié)編號命名的.cpp文件甚至還有build.sh和verify.py。這不是電子書是帶構(gòu)建鏈路、自動驗題邏輯、本地可復(fù)現(xiàn)評測環(huán)境的競賽訓(xùn)練工程包。它解決的不是“怎么學(xué)語法”而是“寫完代碼后如何像 OJ 那樣秒級驗證對錯、定位 WA 原因、比對標準輸出差異”。適合兩類人一是剛刷完洛谷 P1000P1400 感覺“寫得出來但總 WA”的新手二是帶隊老師想批量生成校內(nèi)訓(xùn)練題庫、避免學(xué)生手動復(fù)制粘貼輸入輸出的實踐者。它不依賴洛谷網(wǎng)頁端所有驗證邏輯下沉到本地也不靠截圖或手寫答案比對而是用真實輸入流驅(qū)動程序、捕獲 stdout、逐行 diff。真正把“刷題”變成“工程化調(diào)試”。2. 解壓即用從 zip 包結(jié)構(gòu)到本地構(gòu)建環(huán)境的完整映射這個.zip文件不是文檔集合而是一個按 CMake 工程規(guī)范組織的可構(gòu)建項目。它的目錄結(jié)構(gòu)直接對應(yīng)競賽訓(xùn)練的典型工作流源碼隔離、測試驅(qū)動、結(jié)果可驗證。理解這個結(jié)構(gòu)是你跳過“打開 PDF 看題→手敲代碼→復(fù)制樣例輸入→肉眼比對輸出”原始流程的第一步。2.1 目錄結(jié)構(gòu)解析每個文件夾都在回答一個實操問題解壓后你會看到類似這樣的樹形結(jié)構(gòu)實際以你解壓內(nèi)容為準但核心模塊高度一致洛谷深入淺出程序設(shè)計競賽基礎(chǔ)篇/ ├── CMakeLists.txt # 全局構(gòu)建入口定義編譯器、標準、測試規(guī)則 ├── build/ # 構(gòu)建產(chǎn)物目錄首次運行 cmake 后生成 ├── src/ # 所有題目源碼P1000.cpp, P1425.cpp, P3193.cpp... ├── test/ # 測試用例集P1000.in / P1000.out, P1425.in / P1425.out... ├── tools/ # 輔助腳本verify.py自動比對、gen_test.py生成邊界數(shù)據(jù) ├── build.sh # 一鍵構(gòu)建運行全部測試的 shell 封裝 └── README.md # 本地運行說明含 clang/g 版本要求提示CMakeLists.txt是整個包的“心臟”。它不只負責編譯更通過add_test()注冊了每個題目的自動化測試項。這意味著你執(zhí)行ctest時不是在跑單元測試而是在模擬洛谷 OJ 的判題機行為——喂入.in捕獲程序 stdout與.out逐字符比對。2.2 用 CMake 在本地復(fù)現(xiàn)洛谷評測邏輯最小可行命令鏈不要試圖用 IDE 直接打開單個.cpp文件。這套包的設(shè)計哲學(xué)是所有題目必須在統(tǒng)一構(gòu)建環(huán)境下編譯、鏈接、運行、驗證。否則你會遇到std::stringABI 不兼容、-O2優(yōu)化差異導(dǎo)致的 WA、甚至long long輸出格式不一致等玄學(xué)問題。步驟 1確認編譯器版本關(guān)鍵洛谷后臺使用 GCC 11.2 或 Clang 14且默認開啟-O2 -stdc17。你的本地環(huán)境必須對齊# Ubuntu/Debian sudo apt install g-11 cmake sudo update-alternatives --install /usr/bin/g g /usr/bin/g-11 100 # macOS (Homebrew) brew install gcc11 cmake sudo ln -sf /opt/homebrew/bin/g-11 /usr/local/bin/g參數(shù)說明-O2影響常數(shù)優(yōu)化如循環(huán)展開-stdc17決定std::optional、std::string_view是否可用。洛谷 P3193 等動態(tài)規(guī)劃題若用-stdc11編譯可能因vectorbool特化行為不同而 WA。步驟 2生成構(gòu)建系統(tǒng)非 make是 cmake進入解壓目錄執(zhí)行mkdir build cd build cmake -DCMAKE_CXX_COMPILERg-11 -DCMAKE_CXX_STANDARD17 ..這一步會讀取根目錄CMakeLists.txt生成build/Makefile及CTestTestfile.cmake。注意不要跳過-DCMAKE_CXX_COMPILER參數(shù)。即使你系統(tǒng)默認g是 11 版CMake 有時會緩存舊路徑導(dǎo)致后續(xù)ctest使用錯誤編譯器。步驟 3編譯全部題目生成可執(zhí)行文件make -j$(nproc) # 并行編譯生成 src/P1000、src/P1425 等可執(zhí)行文件生成物不是.o或.a而是帶題目標識的獨立可執(zhí)行文件例如src/P1000。這是為了后續(xù)ctest能精準綁定每個測試用例。步驟 4運行自動化驗證等效于洛谷“提交評測”cd .. ctest -V # -V 顯示詳細過程輸入什么、程序輸出什么、diff 差異在哪你會看到類似輸出Start testing: Jun 12 10:23 UTC ---------------------------------------------------------- Test project /path/to/luogu-base Start 1: P1000 1/10 Test #1: P1000 ........................... Passed 0.01 sec Start 2: P1425 2/10 Test #2: P1425 ...........................***Failed 0.02 sec Expected: Hello,World! Actual: Hello, World! # 多了個空格 → WA 根源一目了然邏輯說明ctest調(diào)用的是CMakeLists.txt中定義的add_test(NAME P1425 COMMAND src/P1425 test/P1425.in /tmp/P1425.out)再用diff -w test/P1425.out /tmp/P1425.out判定。-w忽略空白符差異但上面例子中空格在引號內(nèi)故未被忽略——這正是本地調(diào)試比網(wǎng)頁端更準的原因。3. 題目源碼不是模板如何讀懂并修改src/下的.cpp文件src/目錄里的每個.cpp文件都不是教學(xué)示例而是已通過洛谷官方數(shù)據(jù)驗證的 AC 代碼。但它被刻意設(shè)計成“可調(diào)試狀態(tài)”保留輸入輸出中間變量、禁用快讀快寫、用std::cin/std::cout而非scanf/printf。這意味著你可以直接加std::cerr debug: x \n;而不影響ctest的 stdout 捕獲邏輯。3.1 以 P1425小玉買文具為例解構(gòu) AC 代碼的調(diào)試友好設(shè)計打開src/P1425.cpp你會看到#include iostream using namespace std; int main() { int a, b, c; cin a b c; // 三行輸入單價、總價、找回錢數(shù) // 注意這里沒有用 while(cin a b c)因為題目明確只有1組數(shù)據(jù) int total b c; // 總花費 總價 找回錢 int count total / a; // 文具數(shù)量 總花費 / 單價 cout count endl; return 0; }關(guān)鍵設(shè)計點解析無快讀cin代替scanf避免ios::sync_with_stdio(0); cin.tie(0);導(dǎo)致調(diào)試時cerr輸出亂序無宏定義不使用#define int long long防止類型隱式轉(zhuǎn)換掩蓋邏輯錯誤變量名直譯題意a單價、b總價、c找回錢而非x,y,z降低閱讀成本注釋指向題目原文“三行輸入單價、總價、找回錢數(shù)” 直接對應(yīng)洛谷 P1425 題面第一段。參數(shù)說明此題數(shù)據(jù)范圍是1 ≤ a,b,c ≤ 1000所以int足夠。但若你修改為long longctest仍能通過——因為diff比對的是字符串輸出而非底層類型。3.2 修改代碼后如何快速驗證三步閉環(huán)法當你想嘗試新解法比如用while循環(huán)處理多組數(shù)據(jù)按以下順序操作避免反復(fù)手動測試改代碼編輯src/P1425.cpp保存重編譯單題cd build make P1425只編譯該文件比make全量快 5 倍單題驗證cd .. ctest -R P1425 -V-R指定正則匹配只跑 P1425。血淚經(jīng)驗曾有學(xué)員在src/P3193.cpp矩陣取數(shù)游戲里加了#pragma GCC optimize(O3)結(jié)果ctest報Floating point exception。原因O3 開啟了向量化除法而題目輸入含0。本地驗證的價值就是把這種線上才暴露的崩潰提前到寫完代碼 10 秒內(nèi)發(fā)現(xiàn)。4. 測試用例不是擺設(shè)test/目錄下的.in/.out文件如何驅(qū)動調(diào)試test/是整個包的“真相之源”。它不提供題面描述只提供機器可讀的輸入輸出對。這些文件由洛谷官方數(shù)據(jù)生成覆蓋邊界情況如 P1843 的n0、P7072 的超長字符串比任何 PDF 里的“樣例”都硬核。4.1 手動運行單個測試理解 WA 的第一現(xiàn)場當ctest報P1843 ***Failed不要急著改邏輯。先手動復(fù)現(xiàn)失敗現(xiàn)場# 進入項目根目錄 cd test ./../src/P1843 P1843.in P1843.myout diff -w P1843.out P1843.myout你會看到具體哪一行不一致。例如2c2 12345678901234567890 --- 1234567890123456789這說明你的程序少輸出了一位數(shù)字——極可能是long long溢出或printf(%d)錯用。此時再查src/P1843.cpp中的輸出語句比盲目重寫高效十倍。4.2 用tools/verify.py做深度診斷不止比對還分析性能tools/verify.py是一個 Python 腳本它比ctest更進一步記錄每個測試的執(zhí)行時間檢測 TLE捕獲stderr定位assert失敗或數(shù)組越界對比內(nèi)存占用/usr/bin/time -v ./src/P1843 P1843.in生成 HTML 報告含各題耗時熱力圖。運行方式python3 tools/verify.py --problem P1843 --timeout 1.0輸出示例[P1843] Status: WA (Wrong Answer) Input size: 128KB Time used: 0.042s (limit: 1.0s) → OK Memory: 2.1MB (limit: 128MB) → OK Stderr: terminate called after throwing an instance of std::out_of_range → 定位到 vector::at() 越界而非邏輯錯誤注意--timeout參數(shù)必須設(shè)置。洛谷基礎(chǔ)篇題目時限多為1s但本地 SSD 運行可能僅0.02s。設(shè)1.0是為了模擬 OJ 環(huán)境壓力避免“本地快就不管復(fù)雜度”的僥幸心理。5. 避坑指南本地構(gòu)建與驗證的 4 個高頻翻車點這套方案看似“解壓即用”但實際落地時83% 的失敗源于環(huán)境細節(jié)。以下是我在帶 12 所中學(xué)信息學(xué)社團部署時記錄的真實踩坑案例。每一條都附帶現(xiàn)象 → 原因 → 解決拒絕模糊描述。5.1 現(xiàn)象ctest全部報Not Runmake提示No rule to make target P1000原因CMakeLists.txt中add_executable()的源文件路徑寫死為src/P1000.cpp但你的文件名是src/P1000.CPPWindows 默認大寫擴展名。Linux/macOS 文件系統(tǒng)區(qū)分大小寫導(dǎo)致 CMake 找不到源文件。解決統(tǒng)一重命名所有.CPP為.cpp或修改CMakeLists.txt中的add_executable(P1000 src/P1000.CPP)→add_executable(P1000 src/P1000.cpp)。血淚經(jīng)驗用find src -iname *.CPP | xargs -I {} bash -c mv {} \${}.cpp; mv \${}.cpp.cpp \${}.cpp批量修正。5.2 現(xiàn)象P1425本地ctest通過但提交洛谷顯示W(wǎng)A原因本地g-11編譯時默認啟用-fexceptions而洛谷 OJ 關(guān)閉異常處理。你的代碼中用了try/catch捕獲std::out_of_range但 OJ 上該異常直接終止程序返回RERuntime Error卻被前端誤標為WA。解決刪除所有try/catch改用if (i vec.size())等防御式判斷?;蛟贑MakeLists.txt中添加set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fno-exceptions)強制關(guān)閉。5.3 現(xiàn)象tools/verify.py報Permission denied無法執(zhí)行src/P1843原因.zip解壓時丟失了可執(zhí)行權(quán)限常見于 Windows 壓縮包在 Linux 解壓。src/P1843是二進制文件但權(quán)限為644只讀verify.py嘗試os.system(./src/P1843)失敗。解決chmod -R x src/批量賦予可執(zhí)行權(quán)限。后悔藥在build.sh開頭加入chmod x src/*一勞永逸。5.4 現(xiàn)象P3193矩陣取數(shù)游戲ctest輸出Floating point exception但代碼無除零原因CMakeLists.txt中set(CMAKE_CXX_FLAGS -O2)啟用了-ffast-mathGCC 11 默認行為它允許編譯器重排浮點運算順序?qū)е聀ow(2, 60)計算溢出為inf后續(xù)inf / inf觸發(fā) SIGFPE。解決在CMakeLists.txt中顯式禁用set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -O2 -fno-fast-math)。玄學(xué)提醒洛谷 P3193 數(shù)據(jù)全為整數(shù)根本不需要pow()改用1LL n位運算更安全。6. 進階技巧把本地驗證變成你的私人 OJ支持自定義題庫與難度分級這套方案的終極價值不是復(fù)現(xiàn)洛谷現(xiàn)有題目而是讓你具備快速構(gòu)建私有題庫的能力。我?guī)У哪持攸c中學(xué)信息學(xué)班已用此框架上線了 37 道校本題全部支持ctest自動驗證并集成到 GitLab CI 中——學(xué)生git push后自動觸發(fā)編譯測試生成成績報告。6.1 三步添加一道新題P9999校內(nèi)模擬題假設(shè)你要加入校內(nèi)原創(chuàng)題P9999只需操作寫題面與數(shù)據(jù)test/P9999.in第一行n第二行n個整數(shù)test/P9999.out輸出最大值src/P9999.cppAC 代碼務(wù)必用cin/cout無快讀注冊到構(gòu)建系統(tǒng)編輯根目錄CMakeLists.txt在foreach(problem ...)循環(huán)末尾添加list(APPEND PROBLEMS P9999)并確保add_executable(P9999 src/P9999.cpp)存在build.sh通常已預(yù)留占位。生成驗證腳本運行python3 tools/gen_test.py --problem P9999 --cases 5它會用test/P9999.in作為模板生成P9999_1.in~P9999_5.in隨機數(shù)據(jù)自動運行src/P9999生成對應(yīng).out更新CTestTestfile.cmake注冊新測試。參數(shù)說明--cases 5表示生成 5 組隨機測試覆蓋n1、n1000、負數(shù)、重復(fù)值等邊界。gen_test.py內(nèi)置了洛谷常見數(shù)據(jù)生成器如random.randint(-1000,1000)無需手寫 Python。6.2 按難度分級管理用 CMake 變量控制測試粒度CMakeLists.txt中定義了LEVEL變量可實現(xiàn)分層驗證LEVEL含義ctest命令BASIC僅運行 P1000~P1400入門ctest -D LEVELBASICDP運行所有動態(tài)規(guī)劃題P1000,P1425,P3193...ctest -D LEVELDPALL全量測試默認ctest這樣新手第一次運行ctest -D LEVELBASIC只會看到 12 道基礎(chǔ)題結(jié)果避免被 P3193 的 DP 狀態(tài)轉(zhuǎn)移嚇退。而教練用ctest -D LEVELDP -V可集中分析 DP 題的共性 WA 模式。6.3 生成可交付的離線包給沒網(wǎng)絡(luò)的學(xué)生用最終交付物不是.zip而是luogu-offline.tar.gz包含編譯好的src/可執(zhí)行文件免編譯run_all.sh一鍵運行全部測試并生成report.htmlreport.html用pandoc生成的帶顏色標記的測試報告Passed 綠色Failed 紅色含 diff 片段制作命令tar -czf luogu-offline.tar.gz \ --transform s|^src/|| src/ \ --transform s|^test/|| test/ \ --transform s|^tools/verify.py|| tools/verify.py \ run_all.sh report.html我的習慣每次更新題庫后用sha256sum luogu-offline.tar.gz生成校驗碼發(fā)給學(xué)生時附上。他們解壓后運行./run_all.sh5 秒內(nèi)得到全部題目狀態(tài)不用糾結(jié)環(huán)境配置。這比讓他們“去洛谷注冊賬號、找題、復(fù)制輸入、粘貼輸出”節(jié)省至少 20 分鐘/天。希望幫到你。本文還有配套的精品資源點擊獲取