拓冰建站拓冰建站
首页 / 资讯中心 / 正文

算法竞赛核心盘点:从OJ备战到智能车与Kaggle实战

学算法打竞赛这件事我前后折腾了快十年从大学时第一次接触在线判题系统到后来带队备赛、自己也下场打 kaggle 和数据建模算是一路踩坑踩过来的。身边经常有人问“算法竞赛到底该怎么准备这么多比赛选哪个好”这个问题没有标准答案但有一条主线是共通的——算法本身才是核心资产。标题里这两个词竞赛是场景算法是弹药。场景会变但底层那套功夫永远不会白费。这篇文章我不会跟你扯“某某平台题库刷了三百题就够了”这种话而是把竞赛算法这条路径拆开来讲先理清现在主流的几类算法竞赛究竟在比什么再回到基本功把复杂度、排序、动态规划、搜索剪枝这些高频算法讲透然后用智能车、kaggle、数学建模三个典型场景说明算法怎么落地最后是我在实战里踩过的坑和排错经验。无论你是在校学生准备保研还是工作几年想重拾算法手感或者带队指导学生备赛应该都能从里面找到点能直接用的东西。1. 先搞清楚竞赛和算法到底在比什么很多人一提算法竞赛第一反应就是 ACM 那样的在线判题。但往热搜词里扫一眼会发现实际语境里“竞赛”和“算法”的组合远不止这一种智能车竞赛、数学建模竞赛、kaggle 数据竞赛、华为杯研究生数模这些都被统称为算法竞赛但比的内容差别非常大。不把赛道弄清楚就埋头学很容易出现“辛辛苦苦刷三个月题结果发现比赛根本不考这种题”的尴尬。1.1 算法竞赛的三条主流赛道按我的经验可以把算法竞赛粗略分成三条线。第一条是 OJ 即时竞技型典型代表是 ACM/ICPC、蓝桥杯、各类校内程序设计竞赛。这类比赛的特点是给定明确的问题要求你在限定时间内写程序解决代码跑完判题机立刻给结果。比的是数据结构熟练度、算法设计能力、代码实现速度还有一点很重要的心理素质。这类比赛的算法题库高度集中搜索、图论、动态规划、字符串、数学翻来覆去就这些类型。第二条是数据科学竞赛型以 kaggle、各类大数据竞赛、妈妈杯大数据竞赛为代表。它给你的不是定义良好的算法题而是一堆原始数据和一张任务说明书考的是特征工程、模型选型、调参和防止过拟合。热搜里经常看到的随机森林、深度学习、DQN、PPO都是在这些场景里才高频出现的东西。这类竞赛不看你代码写得快不快而是看你在排行榜上的分数。第三条是应用导向型的复杂赛题包括数学建模、研究生数学建模、华为杯以及智能车竞赛这类软硬结合的赛事。数学建模的题目通常是开放的没有唯一答案你要做的是把现实问题抽象成数学模型选择合适的算法求解最后写一篇漂亮的论文。智能车竞赛则更偏工程重心在嵌入式环境里的图像处理、滤波、控制算法写出来的代码要能在一辆小车上稳定跑完赛道。这三条线对算法的要求有重叠但侧重点完全不同。你在第一条线里刷到的高手换到 kaggle 上未必能立刻排到前面同理在数据竞赛里玩得很溜的人回到 OJ 上写个 KMP 可能还要翻半天模板。但这不代表三条路是割裂的底层的算法思维是相通的只是“应用层”的差异很大。1.2 选路线前先想清楚这几件事我的建议是别急着跟风。选竞赛路线主要看三件事你的时间预算、你的数学底子、你最终想要什么结果。时间预算很好理解。OJ 类的竞赛需要长期刷题每天至少投入一两个小时坚持半年以上才会看到明显变化适合大一大二就开始规划的人。数据竞赛上手相对快但天花板极高而且非常依赖 GPU 资源和数据敏感性适合对统计和机器学习有兴趣的人。数学建模比赛的高强度周期一般在三四天平时主要靠突击式集训适合数学基础扎实、能快速阅读理解文献的人。智能车竞赛则是典型的工程马拉松备赛周期可能长达几个月从焊接电路到调 PID 都在你的任务列表里。如果从“投入产出比”角度看对于大多数普通学生我强烈建议先把 OJ 基本功打好哪怕你最终想走的是机器学习路线。原因很简单数据预处理、特征工程、模型评估这些环节本质上都离不开算法能力。你要是连排序复杂度都说不清楚后面调起模型来会非常吃力。2. 备赛必修数据结构和算法的底层功夫这一节是全文最核心的部分。无论你选哪条竞赛路线这部分都是地基。我见过太多人一上来就抱着一本厚书啃看了三个礼拜还停在第一章“绪论”。问题不在于不努力而是不知道怎么把“知识”变成“技能”。2.1 复杂度分析先学会判断“能不能过”复杂度这块几乎没有一个比赛不考。它决定的是同一个问题你的程序要跑 1 秒还是跑 1 小时直接决定了你能否通过判题。有一个问题在热搜里反复出现“计算算法复杂度时什么时候用 O什么时候用 θ”这里我用最直白的话解释。O 表示的是上界你告诉别人“这个算法最慢不会超过某个量级”θ 表示的是紧确界意思是这个算法在最好和最坏情况下的增长量级是同一个。举个例子插入排序的时间复杂度最坏是 O(n²)但最好情况是 O(n)所以你不能说它是 θ(n²)而归并排序无论是最好还是最坏每一层都要合并 n 个元素一共 log n 层所以它是严格意义上的 θ(n log n)。你可能会问实际判断题目的时间限制时用哪个绝大多数情况只要估算上界就够了。判题机通常配置是 1 到 2 秒的时限而现代 CPU 一秒大概能完成 1e8 次左右的基本运算。所以当你看到一个 n10^5 的问题如果你写了一个 O(n²) 的算法次数是 1e10必超时。这时候就得换思路比如用 O(n log n) 的归并排序代替冒泡或者用 O(n) 的哈希表替代暴力枚举。我用一个生活化的类比来帮助记忆你要从北京去上海O(n²) 相当于骑自行车O(n log n) 相当于坐高铁而 O(n) 相当于坐飞机。自行车再高级也跑不过高铁。算法的选型本质就是根据你要运输的数据规模选一个“能按时到达”的交通工具。2.2 高频算法逐个拆解排序、KMP、DP、剪枝、贪心接下来我挑几个竞赛里出现频率特别高的算法结合实战场景说说它们到底解决什么问题以及你该练到什么程度。先说排序。冒泡排序是教科书最爱讲的因为它直观但竞赛里你几乎不会手写它——那 O(n²) 的性能太拖后腿了。真正常用的是归并排序和堆排序。归并排序的典型价值不只是排序它的分治过程可以用来解决“逆序对”问题这在很多题目里面藏得很深是一个高频考点。堆排序的价值在于能用 O(log n) 的时间拿到全局最大或最小元素这直接催生了优先队列的用法像 Dijkstra 最短路就是靠它提速的。再说 KMP 算法。这是字符串匹配领域的基础算法。朴素的做法是拿模式串和长文本逐个位置比对一旦失败就从头开始浪费时间。KMP 的核心是构建一个 next 数组记录匹配失败后可以跳到哪里继续避免重复匹配。这个算法的代码量不大但 next 数组的三行核心递推很多人背不下来。我的建议是不要死记代码而是亲手用小例子模拟一遍比如在“ababcabab”里匹配“abab”把失配时指针回退的路径画出来理解一次以后你永远都忘不了。搜索这块暴力枚举是入门基本功但竞赛里更讲究的是剪枝。剪枝的本质就是在枚举时提前发现某些分支不可能产生答案直接跳过从而省下大量时间。比如求解数独当你填到一个位置时发现同一行、同一列、同一宫都出现过这个数字那这一个分支根本不用继续递归。A* 算法也是这一类思路的升级版它通过一个启发式估价函数决定先搜哪个方向在寻路类问题里表现极其出色比如迷宫最短路径和游戏中的 NPC 寻路。动态规划是竞赛算法的重头戏也是区分选手水平的分水岭。我用一个经典的爬楼问题来解释。假设你每次可以走 1 级或 2 级台阶问走到第 n 级有多少种走法。暴力递归会指数级爆炸但如果你发现“走到第 10 级的走法 走到第 9 级的走法 走到第 8 级的走法”就可以用一个数组把中间结果存下来从 1 推到 nO(n) 就解决了。这就是动态规划的“最优子结构 重叠子问题”思路。竞赛中的 DP 题目看起来千变万化但核心套路高度一致定义状态、找转移方程、确定初始值、确定遍历顺序。这四步每一步都有常见的坑我在第四节会细说。最后说贪心。贪心算法的思考模式是每一步都选择当前看起来最优的方案最后希望能得到全局最优。但要注意贪心不是万能的只有当问题满足“贪心选择性质”和“最优子结构”时才能使用。比如经典的区间调度在给定多个会议时段中选出最多数量的不冲突会议正确的贪心策略是按结束时间排序贪心地选择最早结束的会议。这个策略能证明正确。但如果你遇到的是“背包问题”这种需要权衡组合的场景贪心就会失效必须用 DP 或回溯来解决。竞赛里的路径就是这样先判断性质再决定策略。2.3 图论与数学类算法从 Tarjan 到匈牙利图论算法是另一个高频考点而且相对更难理解。Tarjan 算法用来求强连通分量特别适合处理“环”类问题。比如判断有向图里是否存在互相可达的节点组或者把一张有环图压缩成一棵树来处理。它的核心思想是利用 DFS 时间戳和 low 值来判断一个节点是否形成了回边。这个算法第一次学可能会绕晕但一旦你手动画一个图、走一遍 DFS 栈整个过程就清晰了。匈牙利算法则是解决二分图最大匹配问题的经典算法。想象一下你有若干岗位和若干求职者每个求职者只能匹配某些岗位要找到一个能让尽可能多人上岗的方案。这就是二分图匹配。匈牙利算法的核心是增广路思想对每个未匹配的左边节点尝试找增广路如果找到就直接增加匹配数。这个算法代码量不多但理解起来比写代码难。竞赛里这类问题常常披着“分配任务”“排课表”的外衣出现识别出二分图模型是解题的关键一步。聚类算法和粒子群算法这类在 OJ 里基本不会出现但它们在数据竞赛和数学建模竞赛里非常常见。聚类解决的是“无标签数据怎么分组”的问题比如 K-means 把样本按距离分成 k 簇。粒子群算法则属于启发式优化算法用来搜索一个函数的近似最优解在建模竞赛的选址、调度类问题中经常被当作黑箱工具调用。我的建议是对于这类算法你不需要从头实现但要清楚它们的原理、参数含义和适用边界否则调参的时候就像在盲人摸象。3. 从纸面到赛场竞赛场景里的算法落地学算法是为了用。这一节我分别用智能车、kaggle 数据竞赛、数学建模三个真实场景还原算法到底是怎么在比赛里发挥作用的。这三个场景对应的热搜词都出现过说明确实是当下大家普遍关注的方向。3.1 智能车竞赛不只是跑个 PID全国大学生智能车竞赛每年吸引大量队伍参与热搜词里也有第二十届、第二十一届智能车竞赛、智能车竞赛规则、获奖名单这些内容。这个比赛表面上是小车跑赛道但实际上对算法的综合要求非常高。先看传感器数据处理。赛道上的小车通常要用灰度传感器或摄像头识别赛道中线但传感器读取的原始信号会有噪声。热搜词里有一个“烟雾传感器 滑动平均滤波算法”这其实反映了嵌入式比赛里的一个通用需求对传感器数据做平滑处理。滑动平均滤波的基本做法是维护一个固定长度的窗口每来一个新数据就丢掉最旧的数据对窗口内的数据求平均。这么做的好处是能抑制高频噪声实现简单在单片机上只占用很小内存。我带队的时候发现很多新手队伍第一个月都在跑迷宫但速度一上来发现小车抖动得厉害原因就是没做滤波到弯道时车身左右剧烈摆动。再看核心控制。PID 控制是智能车比赛里的常客但真正拿高分的队伍一般不会只用一个单环 PID。在弯道上你需要根据当前偏差计算转向量这时比例项 P 负责快速修正积分项 I 负责消除稳态误差微分项 D 负责抑制超调。这里的关键是调参顺序先调 P再调 D最后调 I。很多队伍一开始就三个参数一起调结果是小车以非常诡异的姿态在路上画龙。至于图像处理层面摄像头采集到的赛道图像需要做二值化提取赛道边界再算出中线。更高阶的队伍甚至会用到边缘检测和透视变换把斜视角下的赛道图像转换成正视图再用双边缘提取中线。这些操作听起来很唬人但背后的算法基础还是那几样滤波、二值化阈值选择、连通域分析。可以说智能车竞赛说到底是“嵌入式资源受限环境下的算法简化能力”。你在电脑上跑个算法几毫秒没感觉放到主频几十 MHz 的单片机上每毫秒都要精打细算。所以这道比赛的算法核心与其说是“会用高级算法”不如说是“会把高级算法简化到能落地”。3.2 Kaggle 与数据竞赛算法选型决定胜负kaggle 竞赛这几年热度居高不下热搜词里“kaggle竞赛官网”和“kaggle竞赛”都在榜。这类比赛的核心逻辑是给你一份训练数据你预测验证集和测试集上的结果按分数排名。我在 kaggle 上打过几场最大感受是模型比起特征来说重要性反而靠后。很多人一上来就上深度学习大模型结果 GPU 烧了几天分数纹丝不动而隔壁用随机森林加了一组特征工程的队伍反而轻松上了榜。随机森林是一种基于决策树的集成算法它通过抽样生成很多棵决策树让它们投票决定最终结果。它的优点是能处理非线性关系对缺失值不敏感训练速度快。在一个中小规模的表格数据集上随机森林几乎一定是你的第一个模型。但随机森林有它的局限它很难捕捉非常复杂的时序依赖和空间结构。这时候深度学习就上场了。热搜里反复出现的“深度学习算法”在 kaggle 图像、语音、自然语言处理任务里是绝对主力。CNN 适合图像RNN/LSTM 适合序列Transformer 适合大部分含上下文建模的任务。但深度学习的门槛不在模型结构本身而在调参和训练策略学习率、批大小、正则化系数、数据增强每一项都要反复实验。另外热搜里还有 dqn 算法、PPO 算法和深度强化学习算法这些在 kaggle 上其实不算主流更多出现在类似自动驾驶模拟、游戏策略类竞赛里。强化学习的核心是智能体通过与环境交互获得奖励信号逐步学习最优策略。DQN 用神经网络近似 Q 值函数适用于动作空间离散的任务PPO 则是策略梯度家族的经典算法适用于连续动作控制。我的建议是如果不是竞赛明确要求还是不要从强化学习入门数据竞赛——它的训练不稳定对 reward shaping 极其敏感坑非常多。数据竞赛还有一个隐藏考点验证集的切分和防止过拟合。排行榜上的分数是验证集上的表现但验证集和测试集往往分布不完全一致。我的标准做法是先用分层抽样切一个本地验证集确保类别分布和整体一致然后只用本地验证集做模型选择最后才看到排行榜分数。这样才能避免陷入“刷测试集”的陷阱。这个习惯我吃了不少亏才养成后面一节会详细讲。3.3 数学建模竞赛算法弹药库怎么装填数学建模竞赛从全国大学生数学建模竞赛到华为杯研究生数学建模竞赛题目风格很统一给出一个实际问题让你用数学工具建模、求解、分析最后形成一篇结构完整的论文。这类比赛比的不是单一算法的熟练度而是快速建模的能力和算法的选择眼光。举个例子热搜里“2026年全国大学生数学建模竞赛b题第三问平均定位清除时间为多少”这种问题就典型反映了建模竞赛的二次元气质每题有多个小问每一问都需要用到不同层级的算法。最简单的可以暴力枚举复杂一些的要上排队论模型、蒙特卡洛模拟、最优化求解更复杂的甚至要用到时间序列预测或图论网络流。建模竞赛里的算法弹药库我按使用频率排个序。线性规划和非线性规划几乎天天见像生产调度、资源分配这类问题直接用 scipy 的 linprog 或者 PuLP 求解器就能搞定。聚类算法常用于数据分组和模式挖掘比如样本分类、区域划分。匈牙利算法用于任务指派最优解比如把 n 位选手分到 n 个岗位。粒子群算法用于找不到解析解的连续优化问题比如选址、路径规划。滑动平均滤波则在数据预处理阶段频繁使用用来平滑异常波动。这里我给一个特别实用的建议在建模竞赛里绝大多数问题不需要你从头实现底层算法但你必须知道“什么问题对应什么算法工具”。看到分配问题想到匈牙利或线性规划看到动态变化场景想到微分方程或者仿真看到无监督分组问题想到聚类。这个“对号入座”的能力比任何算法的代码实现都重要。而这恰恰是通过大量阅读优秀论文培养出来的热搜词里“全国大学生数学建模竞赛国赛优秀论文”就是这个道理。4. 比赛实战的操作细节与时间管理这一节我们从刷题和理论学习切换到真实的竞赛现场。无论是 OJ 上的即时判题还是持续几天的数据竞赛、建模大赛时间管理和操作细节往往决定了你在会做和做对之间的差距。4.1 拿到题目后的前 30 分钟读题比写题更重要很多人比赛一开始就狂敲键盘这在我的竞赛经验里是大忌。拿到题目的第一个动作永远是把所有题目都读一遍标注数据范围、时间限制、输入输出格式。对于 OJ 类比赛数据范围直接告诉你这个题的期望复杂度量级。看到 n10^5 基本锁定 O(n log n) 或 O(n)看到 n20 大概率是状压枚举。这个判断失误了后面写得再漂亮也白搭。对于数据竞赛题目描述里往往藏着“评估指标”。是 AUC 还是 F1是 MAE 还是 RMSE这个你必须在写第一个模型前确定因为不同的评估指标对应的优化目标完全不同。比如评估指标是 F1你的模型需要更关注正类的召回评估指标是 RMSE那异常值就容易被放大需要更重视回归的平稳性。4.2 验证“没写错”最有效的办法对拍刷题的时候你会遇到这种情况代码写了手测样例过了但提交上去就是 WA。这时候最有效的排查手段就是“对拍”。具体操作是写一个暴力的、复杂度高但正确性毋庸置疑的程序再拿它和你需要验证的优化程序跑同一组随机生成的测试数据每次跑完比对输出结果。一旦发现不一致要么是你优化程序的边界条件有误要么是暴力程序本身有误这种情况较少。我用一个例子来说明。你写了一个 KMP 字符串匹配总觉得 next 数组有问题。别拿个例一遍遍手推直接写一个朴素的逐位比对匹配作为基准用随机生成的字符串去拍一万次只要有一次输出不一致就顺着那次数据 debug很快就定位到问题。对拍这个方法在竞赛圈里是通用老传统了但这个习惯很多新手根本没建立起导致大量时间耗在低效的手工测试上。4.3 超时问题的排查路线图从算法到常数比赛中几乎人人都会遇到 TLE超时的问题。解决 TLE 分两个层次算法层次和常数层次。算法层次是说你的算法复杂度本身就选错了。O(n²) 跑不过 1e5 的数据哪怕你把代码优化到极致照样超时红。这时候不能指望优化循环展开、用快读这种小把戏解决问题正确做法是换一个复杂度更低的算法。比如把一个 O(n²) 的暴力枚举改成 O(n log n) 的排序加双指针或者用一个 O(n) 的滑动窗口替代暴力这些收益是巨大的。如果算法复杂度已经正确但依旧超时就到了常数优化的战场。核心技巧包括用数组替代 vector 以避免频繁堆分配、开启编译优化开关、用快读替代 scanf/cin、减少递归函数调用。这些优化在最坏的测试数据下可能相差 3 到 5 倍速度有时候刚好是生与死的差别。我在智能车调参的时候也遇到过类似的“差一点点就稳住了”的情况真真体会到“常数优化就是最后一根稻草”。4.4 数据竞赛的时间管理别在提交截止前改模型数据竞赛的比赛周期通常是几周到几个月。很多人有一个非常坏的毛病每次提交前都临时换一次参数甚至换模型然后祈祷分数会涨。这跟赌博没区别。我的习惯是把整个比赛时间按“探索、建模、精调、融合”四阶段来规划。探索阶段花 30% 的时间做数据分析、检查缺失值分布、看特征与目标的关系。建模阶段花 30% 时间把 baseline 跑出来并建立一个可靠的本地验证流程。精调阶段花 30% 时间做特征工程和模型调参但所有的改动都必须记录结果。最后 10% 的时间用来做模型融合比如把随机森林和 XGBoost 的结果做加权平均往往会得到比单个模型更稳定的分数。禁止在最后一小时做重大改动这一点无论强调多少遍都不为过。模型训练需要时间你以为改个参数只加两行实际上可能要重跑 6 个小时截止时间一到你最后交上去的反而是个没训练完的半成品。我见过不止一个队伍在这个环节翻车。5. 我踩过的坑算法竞赛避坑实录最后这部分我说说我多年参赛、带队的真实踩坑记录。这里每一句话都是教训换来的不是从教材上抄来的。先说一个最常见的代码里 int 爆掉。竞赛题目里的数值范围常常不按常理出牌你以为 n 只是 1e5但中间的乘法一乘就过了 2^31 边界结果 WA 了千百回都不知道为什么。我的习惯是参赛前模板里统一用 long long只在确定不会溢出的情况下才用 int。这个习惯救了我很多次。第二个坑是栈溢出。深搜递归在数据量大的时候很容易把系统栈写穿。这时候有两个解法要么改写成显式栈要么在编译命令里手动扩大栈空间。但需要注意的是有些线上判题环境不允许你改编译参数所以最好的办法还是控制递归深度或者在设计算法时就考虑改成迭代式。第三个坑是随机数种子。在 kaggle 或建模比赛里如果你使用随机森林、SVM 这类对随机性敏感的算法必须固定随机种子否则跑出来的结果每次都不一样你连“哪个特征更有效”都无法判断。我在带学生时经常被问“老师为什么我这次跑的 AUC 比上次高 0.01”答案十有八九就是随机种子没固定。还有一个更隐蔽的问题数据泄漏。做数据竞赛时如果你在对目标做缩放之前不小心用到了全部数据的均值去填充缺失值那你已经在用未来信息干扰过去数据了。这类数据泄漏会让本地验证分数虚高看起来像神级模型提交到测试集上却一落千丈。识别和避免数据泄漏是数据竞赛里最考验经验的地方之一。关于数学建模竞赛我特别想提醒的是论文写作比算法模型重要得多。很多队伍算法做得花里胡哨但论文逻辑混乱结果评委根本看不懂你的贡献点在哪里。相反有的队伍模型朴素、但论文结构清晰假设条件交代清楚仿真结果分析到位反而拿到了更高的奖。这里的关键是竞赛是综合能力较量算法只是其中一环表达和呈现是一半的分数。最后补一个很多人忽略的小细节代码模板的日常维护。我建议每个参赛者维护一个属于自己的 algorithm template 仓库里面存三类东西一是基础模板包括快读快写、常用数据结构和算法函数二是历年比赛题目的复盘笔记记录每道题的关键思路和出题陷阱三是竞赛环境的常用配置比如 vimrc 或 VS Code 的 snippets。这个仓库不需要多豪华但每次比赛前花半小时看一眼赛场上敲代码的自信完全不一样。从决定开始准备算法竞赛到真正能在赛场上稳定发挥这个过程没有捷径但也不需要一万小时。我见过不少学弟学妹前两个月浑身是劲第三个月遇到瓶颈就放弃了。实际上算法能力是典型的“复利型技能”前面两个月的积累可能看起来毫无回报但一旦突破某个临界点后面提速会让你自己都惊讶。如果你现在正在为一场比赛纠结选哪条路线或者面对一道题毫无头绪先把这道题拆小把复杂度估算清楚然后用最暴力的方法先跑通再去想优化。这个朴素的方法能带你穿过绝大多数“看起来很难”的时刻。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门