
2023年秋招季我在赛码网上点开了美团第二批编程岗笔试的页面。周围不少同学都倒在了这一轮不是因为题不会而是因为时间分配崩了、环境没调好、甚至读题读偏了。今天我把这场笔试从题型结构、逐题思路到踩坑经验完整复盘一遍给正在备战大厂笔试的同学做一个参考。整场笔试下来我的直接感受是美团编程笔试的难度在互联网大厂里属于中等偏上不考偏题怪题但非常看重你能否在有限时间内把经典考点用得很熟练。先说下基本信息。2023年美团秋招编程岗第二批笔试是在九月中下旬进行的线上考试用的平台是赛码网全程双机位监控一共4道编程题总时长120分钟。语言可以选C、Java、Python等主流的几种。我选的是C周围用Python的也不少。每道题的分值不完全一样从通过率来看前两题属于“签到题”和“基础题”的范畴后两题直接拉开了差距。整体难度梯度设计得比较明显基本就是你平时刷题刷得够不够多、代码写得够不够快的一次集中检验。1. 笔试整体情况与题型结构1.1 赛码网平台与考前准备美团这几年编程笔试基本都走赛码网。这个平台跟牛客、LeetCode有些不一样考前一定要去熟悉一下它的编译和提交方式。赛码网默认是不给你本地方案帮你补全的代码得从头写包括头文件。而且它的输入输出传统得有点像搞ACM时候的OJ得自己处理标准输入输出尤其要注意多组测试数据的场景有些题虽然描述没明说但实际测试是分多组用例跑的。我提前一天做了几件事第一在赛码网上找了一套往年的模拟题跑了一遍确认代码提交、运行、看报错信息的流程。第二把C的读入模板准备好常用的头文件、ios::sync_with_stdio(false)这种加速语句还有getline读取带空格的字符串这类写法全都背熟。第三把网络环境测了一遍确保摄像头、麦克风这些监控设备能正常工作。很多人觉得这些是小事但真到了考场平台不会用、输入输出写错直接就是一道题白给。1.2 题量、时间与分值分布120分钟做4道编程题平均每题30分钟。听起来时间挺充裕但实际根本不是这么回事。因为题目难度是递增的第一道题可能5到10分钟就能解决第三道、第四道题可能耗尽你剩下的所有时间还做不完。所以时间分配的节奏应该是前两题快速拿下最好控制在30到40分钟以内把大块时间留给后两题。分值方面4道题的总分一般是100分但题和题的权重不一样。据说第一批和第二批的题目权重设置偏向于后两题因为后两题是用来区分“能手写中等难度算法”和“只会套模板”的候选人。我看到身边不少同学前两题全对、后两题挂了最终笔试依然能进面试就是因为基础题拿满了、难题也写出了部分用例。所以如果你后两题没思路一定不要空着想想暴力解法哪怕是枚举出一部分小数据的分值也好。1.3 难度定位和第一批、其他大厂横向比较我们私底下把2023年美团秋招前三批编程笔试的题都凑一起复盘过。第二批的难度整体比第一批略高尤其是第四题涉及的知识点从第一批的状态压缩DP变成了树形DP加贪心思维量上了一个台阶。对比字节、腾讯、百度这些大厂秋招笔试美团的题更贴近业务场景喜欢把算法包在一层“店、订单、骑手”之类的皮里面但实际上考的还是经典算法本身。还有一个特点美团笔试的题目描述普遍偏长。不是说题目有多难而是你要花时间去理解业务背景下的输入输出格式。有时候读题5分钟写代码才10分钟剩下一半时间在调边界条件。所以平时做题时要有意识地训练“快速识别题目本质”的能力剥掉业务外壳看到它到底考的是排序、贪心、二分、DP还是图论。2. 四道题逐题复盘与解题思路2.1 第一题签到题但输入输出有陷阱这场笔试的第一道题是典型的签到题考的是数组处理和差分的思想。和美团业务强相关题目大意是一天内有若干个订单每个订单有生效时间段问某个时刻同时生效的订单数。这类题在LeetCode上有很多变体比如会议室II、公交车站点人数统计。拿到题的第一反应是用哈希表统计每个时间点增量然后前缀和求解。因为时刻范围很大1e9量级直接开数组是不现实的但订单量有限所以用差分加离散化最合适。#include bits/stdc.h using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin n; mapint, int diff; for (int i 0; i n; i) { int l, r; cin l r; diff[l]; diff[r]--; } int cur 0, ans 0; for (auto [k, v] : diff) { cur v; ans max(ans, cur); } cout ans \n; return 0; }这里用map是因为它按key遍历能天然保证时间顺序而且diff[r]--表示结束时刻不包含当前点。注意输入输出赛码网的多组输入场景下有些人会写while (cin n)但这道题明确就是单组写了反而可能死循环。这是非常基础的一道题但如果连输入输出都调试半天心态直接崩。2.2 第二题滑动窗口与哈希计数第二题也很典型考的是滑动窗口。大概意思是有一段商家口碑关键词序列要求找出包含全部指定关键词的最短连续子段长度。说白了就是“最小覆盖子串”的变种LeetCode 76题换了个说法。这类题核心思路是双指针维护窗口右指针不断扩大等窗口内满足条件后尝试移动左指针缩小窗口并更新答案。关键是计数方式用一个哈希表维护当前窗口内每个关键词的出现次数另一个变量维护“已经满足要求的关键词种类数”。我当时一次提交就通过了没卡因为这类题我刷题的时候做过至少五遍。但复盘时有同学说卡了很久原因是他用的字符串数组判等每次移动指针都去遍历关键词列表导致复杂度变成O(n*m)直接超时。正确做法是预处理时给每个关键词分配一个索引用一个int数组代替哈希表让查询降到O(1)。#include bits/stdc.h using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin n m; vectorstring words(n); unordered_mapstring, int need; for (int i 0; i m; i) { string s; cin s; need[s]; } int left 0, valid 0, ansLen INT_MAX, start 0; unordered_mapstring, int window; for (int right 0; right n; right) { string c words[right]; if (need.count(c)) { window[c]; if (window[c] need[c]) valid; } while (valid (int)need.size()) { if (right - left 1 ansLen) { ansLen right - left 1; start left; } string d words[left]; if (need.count(d)) { if (window[d] need[d]) valid--; window[d]--; } left; } } cout (ansLen INT_MAX ? 0 : ansLen) \n; return 0; }这题特别能区分“背过模板”和“真会写”的人。背过模板的人能写个大概但边界条件容易错真会写的人能讲清楚为什么window[c] need[c]的时候valid才自增为什么缩窗口时要先判断再减。美团笔试不只看AC不AC后续面试还会问你思路所以不要死记模板。2.3 第三题贪心加排序难在贪心策略的证明第三题开始上强度了。题目大意是美团外卖有多个骑手可以接单每个骑手有固定的接单时间区间和收益一个骑手只能接一单问如何分配使得总收益最大。剥掉业务外衣这其实是一个经典的“区间调度最大化收益”问题只不过不是求最多不重叠区间数而是带权重的版本。带权区间调度最稳妥的解法是动态规划按结束时间排序后dp[i]表示前i个区间能获得的最大收益转移时要么不取第i个区间要么取它并找到前一个不与它冲突的位置jdp[i] max(dp[i-1], dp[j] w[i])。#include bits/stdc.h using namespace std; struct Node { long long l, r, w; }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin n; vectorNode a(n); for (int i 0; i n; i) cin a[i].l a[i].r a[i].w; sort(a.begin(), a.end(), [](const Node x, const Node y) { return x.r y.r; }); vectorlong long dp(n 1, 0); for (int i 1; i n; i) { dp[i] dp[i - 1]; int lo 0, hi i - 1, pos 0; while (lo hi) { int mid (lo hi) 1; if (a[mid].r a[i - 1].l) { pos mid 1; lo mid 1; } else { hi mid; } } dp[i] max(dp[i], dp[pos] a[i - 1].w); } cout dp[n] \n; return 0; }这题我卡了一会儿原因是我一开始想用贪心按结束时间直接选但带权后贪心是错的。比如两个区间一个时长很长但收益小一个时长很短但收益很大按结束时间贪心会先选后者却丢掉了先选前者再叠加其他区间获得更大收益的可能。所以必须用DP。后来我用二分查找优化“找前一个不冲突区间”的过程把单次转移从O(n)降到O(log n)整体复杂度O(n log n)才稳过。这个优化点面试官后续大概率会追问值得仔细理解。2.4 第四题树形DP加贪心综合性强第四题是整场笔试的压轴题大意是美团有若干商家节点构成一棵树每个节点有一个价值我们需要选择若干个节点且不能同时选相邻的两个节点问能获得的最大价值。这就是LeetCode 337“打家劫舍III”的翻版经典的树上最大独立集问题。树上DP的思路很直接每个节点有两个状态选或者不选。如果选了当前节点子节点只能不选如果没选当前节点子节点可以选也可以不选取最大值。用DFS后序遍历自底向上更新状态。#include bits/stdc.h using namespace std; vectorvectorint g; vectorlong long val; vectorlong long dp0, dp1; void dfs(int u, int fa) { dp0[u] 0; dp1[u] val[u]; for (int v : g[u]) { if (v fa) continue; dfs(v, u); dp0[u] max(dp0[v], dp1[v]); dp1[u] dp0[v]; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin n; g.resize(n 1); val.resize(n 1); dp0.resize(n 1); dp1.resize(n 1); for (int i 1; i n; i) cin val[i]; for (int i 1; i n; i) { int u, v; cin u v; g[u].push_back(v); g[v].push_back(u); } dfs(1, 0); cout max(dp0[1], dp1[1]) \n; return 0; }如果你没做过树上DP这场笔试的第四题基本就放弃了。但如果你刷过相关题型会发现它反而是四道题里思路最清晰的一道因为状态转移极其经典没有太多弯弯绕。难点主要在一是要能识别出这是树形结构二是递归深度问题赛码网的C环境如果递归太深可能爆栈所以有的同学用了迭代写法或者手动开栈这也是一个很容易栽的细节。另外这题的数据范围如果很大用邻接矩阵就是自杀行为一定要用邻接表或vector数组。3. 笔试中的失分点与避坑经验3.1 赛码网环境与输入输出的坑赛码网和LeetCode不一样LeetCode帮你处理好了输入输出赛码网全得自己写。如果你平时在LeetCode上刷题刷习惯了突然转到赛码网会非常不适应。最常见的坑有三个第一个是cin和cout默认同步stdio速度很慢。数据量一大明明算法是对的却因为没加ios::sync_with_stdio(false)导致超时。这在第二题里特别明显字符串读入量大的时候不加这行和加这行差距巨大。第二个是读入字符串时如果字符串里可能包含空格必须用getline而不能用cin 。第三是很多同学不知道赛码网会显示编译警告但不一定判错导致看到warning就慌了反复修改耽误时间。3.2 复杂度估算错误从TLE到找出瓶颈笔试里最痛的不是不会做而是“明明能写出解法却被判超时”。我有一次在复盘时帮同学看第三题的代码他的核心算法是对的但查找“前一个不冲突区间”时用了一层线性遍历整体复杂度到了O(n^2)n是10万级别直接TLE。改成二分查找之后立刻AC。所以我给大家一个建议看到题先看数据范围。n 1000O(n^2)随便写n 1e5O(n^2)大概率超时得想O(n log n)或者O(n)的写法n 1e9那肯定不是让你遍历的得考虑数学推导或高维数据结构。美团笔试特别喜欢把n设到1e5以上就是为了测试你是不是真的理解复杂度。数据范围参考表 n 10 - 阶乘级直接枚举所有排列 n 20 - 状态压缩DP可行 n 500 - O(n^3) 可行 n 5000 - O(n^2) 可行 n 1e5 - O(n log n) 必须 n 1e6 - O(n) 或者 O(n log n) 带常数优化 n 1e9 - 必须有数学解法别想遍历3.3 不要空着暴力枚举的保命价值美团笔试的判分规则不是“要么全对要么全错”而是按通过测试用例的比例给分。这意味着哪怕你只会暴力解法也能拿到一部分分数。我当时第四题在树形DP之前先想了一遍暴力DFS枚举所有组合的可能性复杂度是指数级的只能处理n 20的小数据。但美团的数据里恰好有部分测试点是小数据暴力就能过。所以策略很明确遇到难题先写正确率100%的暴力解法保证拿基础分然后如果有余力再优化成正解。千万不要一上来就想着写最优解憋了30分钟没憋出来最后暴力分也没拿到全题白给。3.4 心理节奏管理前两题要稳后两题要狠整场考试120分钟时间看起来不紧张但因为第三题和第四题很容易上头实际体感非常赶。我的节奏是第一题15分钟搞定第二题25分钟搞定剩下的80分钟全部留给第三题和第四题。第三题做了大约35分钟AC第四题写DP加了20分钟调试最后留了25分钟检查之前的代码有没有边界问题。这个节奏合理的前提是前两题必须熟练。前两题基本送分如果这两题你还得慢慢抠那后面的题只能战略性放弃了。所以刷题的时候一定要把那些基础题练到“肌肉记忆”的程度看到题目描述就能快速对应到套路连模板代码都能直接键盘敲出来而不是现场想语法。3.5 多家大厂笔试横向经验美团、拼多多、微众银行秋招笔试大家都会海投我周围就有人连续一周每天晚上都在笔试积累了很丰富的“踩坑经验”。拼多多的笔试难度和美团相当但更偏向于数学推导和规律题赛码网平台也一样。微众银行数据分析岗笔试更注重SQL和Python数据处理和编程岗的题型完全不同。对比下来美团笔试的特点在于业务包装重、后两题综合性强、平台交互传统。所以如果你已经决定投美团笔试前务必做到以下几点一是把剑指Offer和LeetCode Hot 100刷两遍以上二是至少完整模拟三场赛码网的往年真题三是把常用算法模板滑动窗口、二分、贪心、DP、树、图整理成一个自己的代码笔记考前过一遍。4. 针对美团笔试的时间规划与刷题策略4.1 刷题重点排序如果你时间有限想为美团编程笔试做最有针对性的准备我的建议是按优先级排序数据结构基础数组、链表、栈、队列、哈希表这些是必考尤其是哈希表在美团笔试里几乎是每题必用。双指针与滑动窗口第二题级别的常客美团喜欢考字符串和数组的区间问题。排序与贪心第三题级别的常客重点是“如何证明贪心策略是正确的”。动态规划背包、区间DP、树形DP、状态压缩DP是拉分题的主阵地。图论基础DFS、BFS、拓扑排序、最短路优先级略低于DP但也要会。字符串匹配和数学推导占比较少但不能完全不会。4.2 算法模板怎么整理我自己的习惯是准备一个Markdown文件每个算法分“模板代码”“时间复杂度”“适用场景”“常见坑”四部分。考前快速过一遍比临时翻书高效得多。比如二分查找模板我至少背了三种写法找左边界、找右边界、找插入位置。美团第三题里找“最后一个结束时间小于等于当前开始时间的位置”用的就是“找右边界”的二分变形。DP模板我按类型分了背包、线性、区间、树形、状压几个大类每类写一道代表题笔试现场遇到直接往模板里套。4.3 笔试前的模拟训练策略赛码网的过往笔试真题和模拟题是很好的训练材料。考前两周每两天做一套完整的限时模拟完全按考试的标准120分钟、双屏不开其他软件、不查资料。模拟的时候要刻意练习“时间分配”和“暴力保底”的策略不要每次都觉得“再想10分钟就能想出来”很多时候想不出来的题就是耗不起。模拟之后一定要复盘。我会把每道题的错误分类是思路没对、是细节边界漏了、是复杂度写错了、是TLE了每一类错误对应一个改进动作。比如发现自己经常在字符串循环里下标越界那就在模板代码里加上边界判断的标准写法下次直接用。4.4 代码习惯与状态管理笔试的高压环境下代码质量往往比平时差不少主要体现在变量命名随意、逻辑混乱、没有注释、改来改去。我自己的经验是即使时间再紧张也尽量让变量名表意明确。比如cnt、window_valid、max_benefit这样调试的时候不用反复读代码就明白自己原来在干什么。另一个被很多同学忽视的点是休息。美团笔试安排在晚上七点到九点这正好是很多人一天最疲惫的时候。考前那一整天不要安排高强度的刷题任务做一两道简单题保持手感就够了下午好好睡个午觉晚上考试头脑清醒比多做几道题重要得多。我记得那场笔试结束朋友发消息说他第二题明明会做却因为大脑一片空白在left和right指针上绕了半天最后提交时只剩20分钟写第三题。这种状态全靠平时充足的休息和模拟环境下的节奏感来避免。5. 常用代码模板分享这里我再分享几个当时整理出来的高频代码模板都是笔试里可以直接复制的写法节省现场思考时间。5.1 快读快写模板C选手强烈建议背下这套写法能解决85%以上的输入输出性能问题#include bits/stdc.h using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 读入数据量极大时用\n而不是endl // 例如cout ans \n; return 0; }ios::sync_with_stdio(false)关闭了C流和C标准IO的同步cin.tie(nullptr)则取消cin和cout的绑定这两行加在一起读入速度能提升一个量级。但如果代码里混用了scanf和cin就不要加这个否则可能出现读入顺序错乱的诡异问题。5.2 二分查找常用变体// 在升序数组a中找最后一个 target 的位置 int findLastLessEqual(vectorint a, int target) { int l 0, r a.size() - 1, ans -1; while (l r) { int mid (l r) / 2; if (a[mid] target) { ans mid; l mid 1; } else { r mid - 1; } } return ans; }注意二分的边界写法非常多不要混用。我习惯的是while (l r)配合ans记录候选答案这样不容易死循环。5.3 并查集模板美团的后两题偶尔会考到图论和连通性问题并查集是基础。模板很简单但要注意路径压缩和按秩合并。class DSU { public: vectorint parent, sz; DSU(int n) { parent.resize(n 1); sz.resize(n 1, 1); for (int i 0; i n; i) parent[i] i; } int find(int x) { return parent[x] x ? x : parent[x] find(parent[x]); } void unite(int a, int b) { int ra find(a), rb find(b); if (ra rb) return; if (sz[ra] sz[rb]) swap(ra, rb); parent[rb] ra; sz[ra] sz[rb]; } };这里按秩合并不是必须但能防止并查集退化成一条链对性能有微妙影响。5.4 快速幂与取模美团笔试偶尔会出现大数运算快速幂是必备模板。long long qpow(long long a, long long b, long long mod) { long long res 1; while (b) { if (b 1) res res * a % mod; a a * a % mod; b 1; } return res; }很多算法题结果要求模1e97但中间计算如果忘了取模long long都会溢出。我一般规定所有乘法一律先转long long算完就取模不要憋到最后一起取。5.5 树形DFS通用写法树形DP和树上DFS是美团笔试高频考点这个模板必须熟练。void dfs(int u, int fa) { for (int v : g[u]) { if (v fa) continue; dfs(v, u); // 自底向上更新 } }核心就一条用fa参数防止走回头路比vis数组更简洁。而且递归版本只有在树特别深时才需要担心栈溢出一般秋招笔试数据不会极端。6. 考后复盘与更长远的价值笔试结束之后无论结果如何我建议你把四道题重新写一遍写清楚每一道的思路、代码、复杂度然后整理进自己的刷题笔记。这场笔试的题目虽然不是原封不动地出现在后续批次里但考察的知识点是高度重复的。美团后续批次的笔试题目基本上还是这些类型的排列组合。我自己在面试阶段也发现笔试题目其实是在给面试官提供一个“追问的素材”。比如我第三题用了二分优化面试官就追问我“怎么证明二分查找的正确性”“如果区间右端点改成开区间你的代码要改哪里”。这些问题如果你笔试时只是照着模板抄不深入理解面试环节就会露怯。复盘的价值还在于你可以通过一次笔试摸清自己的短板。我当时的短板是树形DP不熟练第四题写得磕磕绊绊。于是笔试后的两周我专门刷了树形DP的专题把树的重心、树的直径、树上最大独立集、树上背包全部过了一遍。后来面另一家公司时笔试正好考了一道树上路径最大值问题我用类似思路很快AC。所以失败了也不可怕关键是从每一次笔试中提取可复用的经验。最后多啰嗦一句关于备战期间心态的话。笔试只是秋招过程中的一个关卡不是终点。一次笔试成绩不理想并不代表你能力不行很多时候是状态、运气、以及对平台和环境熟悉程度的综合结果。保持稳定刷题的节奏保持规律作息把每一次线上笔试都当成一次免费的实战模拟积累的临场经验最终会在某个关键时刻帮你拿到想要的offer。