简介ACM竞赛训练题集包面向备战ICPC/ACM的选手、算法学习者和需要系统刷题的编程爱好者集中收录大量竞赛题目与解题源码可辅助建立算法知识框架、开展专题训练。压缩包共281个文件整体约870.44MB除主体C源码(.cpp)外还包含较多Visual Studio工程辅助文件(.suo/.vsidx/.ipch/.db/.lock)和2个txt文件可在还原工程环境后直接阅读、调试与对比。内容覆盖基础排序查找、递归迭代、栈队列堆与树图结构、DFS/BFS、Dijkstra与Prim/Kruskal、背包问题与最长公共子序列、KMP与字符串匹配、组合数论、贪心、回溯、分支限界及位运算等高频考点知识面完整。通过研读多份cpp实现可以拆解题意与状态转移观察边界条件和复杂度优化比较不同思路并沉淀自己的题解模板。目前已有874人浏览学习适合从入门到赛前冲刺不同阶段的备赛者参考。1. 先立一个判断ACM题集的价值不在“超多”而在“分层”看到“ACM题集”四个字很多人的第一反应是囤资源下载一个几百 MB 的压缩包里面躺着上万道题和源码便觉得胜券在握。但真刷过竞赛题的人都知道题集越“超多”越容易让人在选择困难中原地打转今天打开哪道这题合不合适要不要跳过我的判断是ACM题集的真正价值不是“给你一万道题”而是“把一万道题分好类、标好难度、配上参照源码”。它应该成为从 acm入门 一路走到现场赛的弹药库而不是一个数字好看的文件包。这篇笔记会讲怎么把手头的题集拆成专题路线怎么读解题源码而不是背源码以及那些让多数人三个月就放弃的坑。适合备赛 ACM 的学生、准备公司机试的求职者以及想找一条主线的自学者。2. 搭一套自己的ACM训练题集专题拆解、难度锚点与目录结构2.1 为什么按专题刷比顺着OJ热度刷有效三大专题源怎么选顺着 OJ 的“题库列表”从第 1 题往第 1000 题刷是最容易把题集成成“一次性消费”的用法。原因有两个一是题库按收录顺序排列靠前的题偏模板、同质化严重后面的题难度跳跃又大二是没有专题上下文遇到一道题时你很难快速判断它考哪块知识复盘时也没有“归因”的抓手。按专题刷的本质是把“我刷了多少题”换成“这个知识点刷透了没有”后者才是比赛和机试真正考察的东西。专题源的选择常见做法有三类。第一类是 OJ 自带的分类功能比如 Codeforces 按 tag 过滤题目牛客的专题训练把二分、DP、图论拆成单独训练营适合想快速搭骨架的人。第二类是网上流传多年的“POJ 分类”“HDU 题目分类”这类经典清单题都是经过时间检验的经典题缺点是年代偏久、部分题在新 OJ 上没有数据。第三类是历年真题集合比如把历届 ACM 全球总决赛真题按年份归档拿来当模拟赛素材适合已经有基础、想拉高强度的人不适合还在入门期的新手。我的建议是以第二类分类为骨架用第一类 tag 去补充新题把第三类真题作为每周一次的模拟素材。这其实是训练计划的选料问题和你用什么语言关系不大哪怕你只写 C 语言专题拆解的方式完全一样。关键是给自己定一条主线第一周基础数据结构第二周图论第三周动态规划而不是每天从题单顶部随机抽一道。没有主线一万道题只是一万道题有了主线一万道题才变成一条可以攀爬的阶梯。2.2 给每道题定难度锚点A/B/C 分级与配比题集一旦超过 2000 题“难度”就必须显式写进文件名或清单里否则你会在某一天打开一道区域赛金牌题然后怀疑人生。我一般会给每道题打三个等级直接体现在题单的标签里后续安排训练节奏时一眼就能挑出该刷哪道。等级适用阶段用时预期在训练里的作用A刚入门单知识点30~60 分钟固定模板建立手感B有一定基础知识点组合1~2 小时训练变形和复杂度分析C区域赛/总决赛真题2 小时以上模拟赛练心态和查漏配比上我推荐 4:4:2也就是 A 类四成、B 类四成、C 类两成。不要小看 A 类很多人栽在复杂度分析和“小细节”上就是因为 A 类刷得太少一上来就钻难题也不要贪 C 类C 类题目经常需要多步推导一天消化一道已经非常出色。每道题在本地记录里可以写成如下格式- 题目P1044 栈洛谷 等级A | 状态AC | 错因递推边界写错 - 题目Codeforces 1462E2 等级B | 状态WA→AC | 错因组合数取模前没预处理逆元这样当你面对“超多 ACM题集”时看到的不是一个让人头皮发麻的长列表而是一条条可执行的小任务今天两道 A 类热手一道 B 类变形每周一道 C 类或历届总决赛真题拉练。难度节奏稳住了训练才不会忽冷忽热。分级这件事花不了多少时间但它是整套训练系统的地基没有分级后面所有统计和复盘都会失真。2.3 用目录把题集落地可复制的结构与 README 模板分类思路再清晰没有落地的文件结构很快就会乱回原点。我的习惯是在本地建立一套和训练计划同步的目录题集源码按专题存放文件名带上状态后缀。下面这个结构是我重装过几次机器后沉淀下来的可以直接抄mkdir -p acm-train/{00_basic,01_ds,02_graph,03_dp,04_string,05_math,06_contest,07_review} cd acm-train touch README.md命名规则是编号_专题缩写00_basic放输入输出、前缀和、差分、二分01_ds放栈队列堆、并查集、线段树、树状数组03_dp放线性 DP、背包、区间 DP、树形 DP06_contest专门存区域赛和历届全球总决赛真题07_review是错题和待复写题的回收站。README.md里写清楚每个子目录放什么、当前训练到哪一周格式不限但必须有“专题、难度、AC 状态、错因”四个字段。提示目录层级不要超过三到四层太深反而会在提交记录时频繁切换路径影响刷题流。这里有个容易被忽略的细节状态后缀。我要求所有源码文件名以_AC.cpp或_WA.cpp结尾表示最后的评测结果。目录一拉出来每个专题的红绿比例一目了然后面做吸收度统计时脚本也能省很多事。目录结构不是给人看的是给未来的自己和统计脚本看的想明白这一点组织形式自然就简单了。3. 读解题源码的正确姿势先分类型再抠三件套3.1 拿到源码先分类型模板、解法和一次性代码题集附带解题源码最常见的翻车方式是打开一个 AC 代码从头到尾读一遍觉得“学到了”关掉页面三天后遇到同类型题依然写不出来。这不能怪记性差是因为读代码时没有把自己放在“写代码的人”的位置。我的方法是拿到任何一份源码先花三分钟判断它属于下面三类中的哪一类。第一类是模板代码比如快速幂、并查集、Dijkstra 的堆优化版、线段树板子这类源码的价值是“直接抄进自己的模板库”。第二类是解法代码它解决的是某个具体题目的建模思路比如“怎么想到用二分答案”“如何用单调栈维护区间最小值”这类源码的价值在注释和变量命名里。第三类是一次性代码比如为通过某道恶心的模拟题而写的特判堆它几乎没有任何复用价值读它纯属浪费时间。区分方法很简单看头文件和代码长度。模板代码通常只依赖标准库核心函数体和算法骨架强对应一次性代码往往会夹很多魔法数字、一层层 if 嵌套、甚至为了卡常写的诡异循环。对后一种我一般只看它过了哪些测试点根本不逐行走读。先判断“这是哪一类”比打开代码就开始逐行硬啃重要得多这也是熟手和新手在源码阅读效率上拉开差距的地方。3.2 提取“三件套”C 竞赛骨架、快速 IO、类型别名一套干净的 C 竞赛骨架是所有 ACM题集 源码里的最大公约数。不论题目是什么它都可以直接作为起点后续只需要往里面填具体算法。我解题的默认框架如下#include bits/stdc.h using namespace std; using ll long long; const int INF 0x3f3f3f3f; const int MAXN 1e6 5; int a[MAXN]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 单组: 直接读 // 多组: while (cin n) { ... } // 带 T: cin T; while (T--) { ... } return 0; }三段注释分别对应三种输入模式写代码时不用每次临时想。ios::sync_with_stdio(false)和cin.tie(nullptr)是把 C 标准输入输出与 C 的 stdio 解绑避免双缓冲导致变慢这是新 OJ 上跑 C17 的常规加速姿势。using ll long long给长整型起别名因为竞赛题里int溢出是 WA 的重灾区。MAXN按题目数据范围上限开比如题目说 n ≤ 10^6就开 1000005宁可多几个下标也不能越界。注意bits/stdc.h这个万能头文件只保证在 GCC 系编译器里可用如果遇上要求严格的旧 OJ 或非 GCC 环境换回cstdio、cstring、vector等标准头文件。如果你习惯用 C 语言写这套骨架同样成立把cin/cout换成scanf/printf头文件换成stdio.h和string.h就行acm 竞赛题目 c语言 版本的好处是控制力强、编译快坏处是字符串类题目写起来偏慢实际使用时按题目类型取舍。对大多数人来说C 的 STL 能省下的调试时间更值得。3.3 15 分钟伪码还原法从源码反向推思路并复写模板提取完之后真正吃掉一道题的做法是“伪码还原”。我给自己定的流程是这样先看题目不看源码自己在纸上写五到八行伪码写不出就硬憋十五分钟然后打开源码对比它的核心算法与我伪码之间的差异点再把差异点抄下来合上代码重新用代码实现一遍。整个流程控制在四十分钟以内。这个做法背后是一个很朴素的道理阅读源码的过程里“识别算法的骨架”比“看懂每一行在干什么”重要得多。源码里的关键变量名通常会暴露思路——出现dp[i][j]多半是动态规划出现vis[]和dfs()多半是搜索出现dis[]和priority_queue多半是最短路。你可以对照这份速查表快速建立代码与算法的映射源码特征大概率算法方向读源码时重点看dp[][][]、max/min转移动态规划状态定义与转移方程vis[]、dfs()、stack搜索/回溯剪枝条件与结束条件dis[]、priority_queue最短路或 Dijkstra松弛条件和堆优化写法parent[]、find(x)并查集路径压缩与按秩合并prefix[]、差分数组前缀和/差分区间更新时如何标记和还原复写时不要求逐字节复刻源码只要核心思路一致即可。这一步没法通过“多读”练出来只能通过“多写”逼自己把源码里的抽象思路还原成自己的实现。刚开始会非常慢一道 B 类题可能要折腾一整天但坚持二十道题之后你看到新题会本能地判断“这题大概率是 DP状态可能是这样做”而不是先打开题解碰运气。源码是参照物不是导航仪这个身份一旦搞错刷题就成了抄题。4. 把题集喂给评测机acm模式、输入输出与跨OJ迁移4.1 本地跑通不等于 OJ AC评测机的三个边界样例过了、本地运行也对一交上去就 TLE 或 WA这是题集练习里最常见的“翻车”瞬间。踩多了以后你会发现OJ 和本地至少有三个边界是源码里不写、但评测时一定会卡的。第一是时间边界。评测机的 CPU 通常比个人电脑弱一截你的桌面 CPU 上跑 1 秒的暴力搜索到 OJ 上可能就是 5 秒起步而时限大概率只有 1 到 2 秒。所以“输出正确”完全不能当成复杂度达标的证据。判断方法很粗暴把题目里的 n 上限代入你的算法复杂度如果超过 10^8 量级几乎必然需要优化。第二是内存边界。很多人喜欢在函数里写int dp[2000][2000];本地能跑OJ 却可能直接 MLE 或爆栈竞赛源码的常规做法是把大数组放到全局区或者用vector动态分配。第三是输入输出边界多组数据、EOF 结束、行尾空格这些细节本地样例往往覆盖不到。一个实用的本地自查动作是造一组 n 取最大值的数据用time ./a.out data.in看真实耗时再乘三到五倍才是一个保守的 OJ 耗时估计。没有这个动作题集刷得再多复杂度意识也很难建立起来。4.2 “acm模式”输入输出模板单组、多组与带 T 组竞赛做题和日常开发最大的区别是输入输出完全由题目描述决定OJ 不会替你处理。这个自己处理全部 IO 的玩法正是竞赛选手口中的“acm 模式”。与之相对的是“核心代码模式”——你只写核心函数力扣的题是这种华为OD 的 Java 机试 C 卷里部分题目给的也是这种代码框架而 ACM 竞赛题基本都属于 acm 模式一些公司机试也会要求题目按 acm 模式提交。所以审题时第一件事是确认模式别把函数写完才发现还要自己拼main和读入。针对三种最常见的输入形式我平时直接套下面这套模板#include bits/stdc.h using namespace std; const int MAXN 1e6 5; inline int readInt() { int x 0, f 1; char c getchar(); while (c 0 || c 9) { if (c -) f -1; c getchar(); } while (c 0 c 9) { x x * 10 (c - 0); c getchar(); } return x * f; } int main() { int n; // 模式一单组数据 // scanf(%d, n); // 模式二多组数据读到 EOF 结束 while (scanf(%d, n) ! EOF) { /* 处理 */ } // 模式三第一行是 T后面有 T 组数据 // int T; while (T--) { scanf(%d, n); } return 0; }readInt()是一个标准的快读函数它的作用是在数据规模超过 1e6 时替代scanf因为字符逐位解析的getchar路径比格式化输入快不少。参数上要注意MAXN必须大于等于题目上限快读函数里对负数做了处理如果题目保证全正数可以把f相关逻辑删掉减少分支。提示数据规模低于 1e6 时直接scanf就够了不要为了追求快读破坏代码可读性。IO 这块是最不值得丢分的但也是最容易在 acm 模式下丢分的地方本地样例不会覆盖“多组数据”和“超大输入”这两个场景所以每道题提交前我都先把题目输入描述里有没有 “multiple test cases” 看清楚。4.3 换 OJ 后 WA三个必查的迁移点同一个题集里的题可能来自不同 OJ你在一家 OJ 上 AC 的代码搬到另一家可能莫名 WA。多数情况不是算法变了而是下面这些迁移点被忽略了检查点常见踩坑排查方式整数宽度32 位int溢出涉及累加、乘法的变量改用long long输入函数gets()已弃用改用cin.getline()或fgets()输出格式行尾多空格、缺换行对拍输出用diff逐字节比较C 标准OJ 用 C11 而你写 C17 语法提交前看 OJ 的编译器版本栈空间局部大数组爆栈大数组改为全局或vector在这些检查点里最玄学的是“本地对拍全对一提交就 WA”。我会用三行对拍命令用diff比较自己的程序和暴力程序在随机小数据上的输出python3 gen_data.py data.in ./a.out data.in my.out ./brute data.in std.out diff my.out std.out echo OKgen_data.py生成随机小数据brute是一个写死的暴力验证程序a.out是待验证的题集源码。只要 diff 出现差异就说明代码里存在一个只在特定输入下触发的边界问题如果小数据全对但大数据 WA优先怀疑整数溢出和数组越界。这套组合拳能排掉七成以上的“换 OJ 就 WA”。5. 刷ACM题集常见问题排查五个典型坑的现象、原因与解决5.1 囤积 1 万题不筛选刷了三周还在入门现象题集里的文件确实很多每天打开也是“随便做两题”但做来做去都是同类简单题进度条永远卡在 10%。原因缺少筛选和分级。对这个人来说题集只是一堆待办事项不是一条有上升路径的训练链路。没有 A/B/C 分级就没有“今天练什么”的决策依据于是每天都会选最容易的那道产生一种“我在刷题”的错觉。解决先花一整晚按 2.2 的 A/B/C 规则给前 200 道题打标只保留能落进专题目录的题目。剩下没标签的题先冻结不要纠结是否错过好题——好题会在后续训练里以新面目重新出现。囤积症的解法不是“多而全”而是“筛选出一个能走完的列表”。5.2 AC 后不写题解一个月后同一道题像新题现象AC 的那一刻成就感很高随手往题库一扔就刷下一道。一个月后翻回来觉得有点眼熟但完全写不出。原因AC 只代表“当时会”代码里的思路没有被压缩成可回忆的笔记。竞赛记忆靠的是后天主动回忆不是“刷过”这个动作本身没有笔记一个月后那道题和一道新题几乎没有区别。解决每道 AC 题在 README 对应行写一句错因再用“acm日记”的方式记录三行思路一句话、复杂度一句话、卡住点一句话。日记不是长篇题解是给未来的自己留一个提取线索。我用日期做文件名按周归档回看时能看到自己从一路 WA 到稳定 AC 的曲线这种正反馈比收藏夹里的资源更真实。5.3 源码直接看、看完就关没有复写闭环现象看到别人的 AC 源码就觉得自己懂了收藏夹里存了一堆“以后要看”的链接但三天后遇到同思路的题依然不会写。原因阅读是被动吸收写代码是主动输出。不经过复写的源码理解是“假理解”大脑记住了“好像看过”却记不住“该怎么写出来”。解决强制使用 3.3 的 15 分钟伪码还原法至少复写一遍。复写时不许回头翻原码如果复写过程超过四十分钟就把这道题标记为“待二刷”扔进07_review目录。刷题集的核心不是刷完多少题而是有多少题进入了 review 清单——真正需要二刷的题才是题集留给你的增值部分。5.4 只刷基础标签比赛真题碰都不敢碰现象专题练得很熟练一换到混合场景题比如历届 ACM 全球总决赛真题就卡死觉得题目“怪”。原因所有标签都是按知识点组织的而现场赛真题是按场景混合的。习惯了按标签导航刷题的人一离开“这题考二分”的提示牌就不知道怎么定位考点。解决每周固定做一道 C 类真题不求 AC只求能写出“这道题考了哪些知识”的拆解。拆解比 AC 重要因为它逼着你把几个知识点组合起来看。做错不可怕怕的是错完不知道错在哪个知识点的边缘C 类题就是用来暴露这个边缘的。5.5 输入输出本地对、线上 WA 的场景现象本地样例完全正确交上去第一发就是 WA而且看不到任何输出差异。原因本地根本没测多组数据、没考虑 EOF、或者输出格式里多了一个空格。样例只是最低保障它不会覆盖所有边界输入。解决回到 4.2 的模板检查模式题目有没有 T是不是读到 EOF 结束输出要不要每个 case 前打印Case #x:自己构造极端输入——n1、n上限、数据里带负数再用 4.3 的 diff 对拍脚本排掉格式问题之后才回头查算法逻辑。5.6 五个坑的共同根源把“收藏题集”当成“训练系统”回头看这些坑背后其实是同一个错误把“拥有题集”等同于“做过题集”。资源囤得越多训练的正反馈反而越弱因为下载和分类本身就会制造一种虚假的掌控感。我当年也干过这种事后来把题集从两万道删到只剩精心挑过的三千道训练效率反而翻倍。这不是说不要囤资源而是说题集是原材料必须经过筛选、分级、归档、回溯四个环节才能变成训练系统。没有这套流程题集越大越容易让人在无限选项里瘫痪有了这套流程“超多”只是给你足够的选择空间而不是焦虑来源。6. 用“错题回溯 复写抽样”验证题集吸收度6.1 让吸收度可见一个按专题生成报告的统计脚本题集刷得越多越需要一个客观的“吸收度”数字不然很容易自我感动。我每隔两周会跑一个脚本遍历题库目录统计每个专题的文件数、AC/WA 状态和最近修改时间import os from pathlib import Path from datetime import datetime root Path(acm-train) for sub in sorted(root.iterdir()): if not sub.is_dir(): continue files list(sub.rglob(*.cpp)) ac sum(1 for f in files if AC in f.stem) wa sum(1 for f in files if WA in f.stem) recent max((f.stat().st_mtime for f in files), default0) print(sub.name, total:, len(files), AC:, ac, WA:, wa, recent:, datetime.fromtimestamp(recent).date() if recent else none)脚本逻辑很简单按专题目录分组统计文件名里带_AC和_WA的文件数量再取该目录最近修改时间。如果某个专题超过 14 天没有新文件那它就是你训练版图里的一个“黑洞”如果某个专题 WA 比例超过三成说明基础不牢需要把等级调回 B 甚至 A。参数上文件名的状态标记必须严格执行否则统计会失真。6.2 两周抽样复写法让“旧题”真正变成“已掌握”只有统计还不够我还会配合一个简单的抽样复写每两周从07_review目录里随机抽 5 道题在 90 分钟内限时复写要求不看原码、不查笔记AC 率超过 3 道才算合格。不合格的题继续留在 review 里等下一轮再抽。这比“从头到尾再过一遍题集”省时间也比“只刷新题”更能防止遗忘。这个习惯是从一次现场尴尬里长出来的有一年模拟赛我碰到一道曾经 AC 过的原题愣是 30 分钟没写出转移方程赛后复盘发现自己在题集里刷过它却没有复写过。现在每刷完一个专题我都会顺手把几道典型的旧题扔进 review形成“刷新题→写日记→两周复写”的闭环。没有这个闭环题集里的源码只能算收藏算不了实力。希望这一套“题集 源码 回溯”的组合能帮到你少走我当年走过的弯路。本文还有配套的精品资源点击获取