十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

2023美团秋招第三批笔试复盘:高频算法题型与备考策略

2023美团秋招第三批笔试复盘:高频算法题型与备考策略 2023年美团秋招编程岗的第三批笔试我帮不少准备秋招的同学完整复盘过。这批题最大的价值不在于难而在于它把互联网大厂笔试的“标准打法”展示得很清楚业务场景套壳、经典算法打底、难度从签到题一路爬到压轴题。准备后端岗、算法岗的同学尤其是刚进入秋招节奏还没摸清笔试套路的人看完这篇应该能对“美团笔试到底考什么”有个非常具体的认知也能顺着这套思路去调整自己的刷题方向。1. 为什么第三批笔试值得单独拿来说1.1 批次与难度不只是时间不同美团秋招笔试是分批次滚动进行的第三批通常落在9月下旬到10月初。很多人觉得越早的批次越简单其实不完全是这样。第三批的出题风格已经非常稳定不再像第一批那样偶尔会出现一两道试探性的偏题整体难度曲线被刻意打磨得很有区分度前两题给大多数候选人保底第三题开始拉开差距第四题则是真正留给头部候选人的。这个时间段还有一个现实因素HC招聘名额已经消耗了一部分笔试的筛选作用会被放大。所以你会明显感觉到第三批的题目结构设计得特别“有梯度”保证有实力的候选人能完整展示水平同时也能把只靠背诵答案的人筛出去。换句话说第三批是“标准考卷”的典型样本复盘它比复盘第一批的试水卷更有参考意义。1.2 美团笔试的风格业务场景包装算法美团笔试编程题有一个很明显的风格题目一定会给你讲一个小故事。订外卖、送餐、商家满减、团购套餐、仓库拣货、骑手排班……你会发现每道题都不是干巴巴地给你一个数组让你算而是先铺一段业务背景再告诉你输入输出格式。剥开故事外壳底下基本都是经典模型排序加贪心、二分答案、动态规划、拓扑排序、图的最短路径。这种命题方式的目标不是考偏题怪题而是考你“能不能快速把业务需求抽象成已知算法”。说白了笔试考的是翻译能力把“骑手要把订单分成若干批次”翻译成“把数组分成k段最小化最大段和”把“服务之间有依赖并行编译”翻译成“拓扑排序求最长路径”。谁的翻译速度快谁的边界考虑得全谁的分就高。2. 整体结构与时间分配2.1 题量与分值的分布第三批笔试的整体结构通常是4道编程题总时长120分钟个别场次会压缩到90分钟。难度排列非常标准第1题偏签到考察基本的模拟和输入输出处理第2题和第3题是分水岭基本落在二分答案、贪心、动态规划这一类高频考点上第4题是压轴题经常是图论相关或者DP加深一层用来区分高分段。分值分布一般是前低后高第一题可能只占20分左右压轴题能到30分甚至更多。但这里要提醒一句分值高不等于你要花最多时间因为压轴题往往是“想不出来就是想不出来”而前两题是“想想就能做出来”。把保底分拿稳再拿部分分总分反而会好看。2.2 时间分配建议我建议的时间分配是这样第1题15分钟以内必须AC超过这个时间先放下标记一下回头再看。第2题20分钟左右这是必争之地一般考二分或者贪心。第3题30分钟以内把能拿的测试点全部拿到不要在一处卡死。第4题剩余时间都给它但是目标不是AC而是尽量多过几个样例。如果考试时长是90分钟那第3题要压缩到20分钟左右因为前面输入输出不熟练的话读题和调试都会额外吃时间。笔试是看总分的不是看单题完美度。死磕一道题导致后面简单分拿不到是每年笔试最常见、最亏的一种死法。3. 典型题目复盘从签到题到压轴题下面这一部分我按2023年美团秋招编程岗第三批笔试的命题风格把最有代表性的四类题做成可运行的复盘例题。每一道都给出了完整的题目描述、输入输出格式、样例、解题思路以及Python实现。建议你照着代码手敲一遍比光看理解深得多。3.1 第一题单窗口订单排队模拟题目描述一个外卖门店只有一个制作窗口。现在有n个订单每个订单有一个到达时间 arrive[i] 和制作时长 cost[i]。窗口同一时间只能做一个订单订单按照先到先服务的原则处理。问最后一个订单完成的时刻是多少。输入格式第一行一个整数n表示订单数量。 第二行n个整数表示每个订单到达时间。 第三行n个整数表示每个订单制作时长。输出格式一个整数表示最后一个订单完成的时刻。样例输入4 1 2 4 5 3 2 1 2样例输出9解题思路这道题就是纯模拟。把到达时间和制作时长按订单配对按到达时间从小到达排序然后用一个cur变量记录当前窗口空闲下来的时间。处理每个订单时窗口要先等到订单到达如果cur小于到达时间就跳到到达时间否则继续用cur。然后加上制作时长就是该订单完成的时间。如果某个订单已经在排队过程中到了而是先到先服务那排序后直接累加即可不需要用到优先队列。优先队列是“谁先完成谁先服务”的模型这里用不上。代码实现def solve(): n int(input()) arrive list(map(int, input().split())) cost list(map(int, input().split())) orders sorted(zip(arrive, cost)) cur 0 for a, c in orders: if cur a: cur a cur c print(cur) if __name__ __main__: solve()这类题考点很直接排序、模拟、循环。它的坑在于别把订单弄丢也不要自作聪明去反向排序。很多同学第一题写很久往往不是因为算法难而是输入读串行或者样例都没手动验。第一题只要稳住基本就是白给的分。3.2 第二题配送批次拆分二分答案题目描述骑手一天有n个订单每个订单有一个包裹重量 w[i]。现在要按照订单的原始顺序把订单分成连续的k个批次配送每个批次里所有订单的重量之和称为该批次的负载。你希望最重的那个批次的负载尽可能小问这个最小值是多少。输入格式第一行两个整数n和k。 第二行n个整数表示每个订单的重量。输出格式一个整数表示最重批次负载的最小值。样例输入5 3 1 2 3 4 5样例输出6样例解释可以分成三段[1,2,3]重量为6[4]重量为4[5]重量为5最重的批次是6。这是所有分法中最大段和最小的情况。解题思路看到“最小化最大值”或者“最大化最小值”第一反应就应该是二分答案。这里我们二分一个负载上限mid然后贪心地从左到右扫描数组看能否在每组负载都不超过mid的前提下把订单分成不超过k组。判断函数怎么写用一个计数器cnt表示当前已经分了几个批次用s表示当前批次累加的重量。逐个扫描订单如果s加当前订单重量超过了mid说明当前批次放不下了需要新开一个批次cnt加1同时s重置为当前订单的重量。如果整个过程中cnt不超过k说明mid可行。二分的下界是数组中最大的单个订单重量因为任何一个批次至少要装下一个订单上界是所有订单重量之和因为最坏情况就是所有订单装在一个批次里。代码实现def check(mid, w, k): cnt 1 s 0 for x in w: if s x mid: cnt 1 s x else: s x return cnt k def solve(): n, k map(int, input().split()) w list(map(int, input().split())) left max(w) right sum(w) while left right: mid (left right) // 2 if check(mid, w, k): right mid else: left mid 1 print(left) if __name__ __main__: solve()二分边界是这道题最容易出问题的地方。我这里统一用的模板是当check(mid)可行时把右边界收缩到mid否则把左边界收缩到mid1这样最终left和right相等时就是答案。不要用那种一会mid1一会mid-1的写法和另一个模板混着记笔试现场很容易搞乱。3.3 第三题预算内的热量最大化0/1背包题目描述公司团建采购零食货架上有n种零食每种零食只有一个价格是p[i]热量是v[i]。你手上有预算B元在不超预算的前提下买到的零食总热量最大是多少。输入格式第一行两个整数n和B。 第二行n个整数表示每种零食的价格。 第三行n个整数表示每种零食的热量。输出格式一个整数表示能获得的最大总热量。样例输入4 10 2 3 5 7 3 4 6 8样例输出13解题思路这就是经典的0/1背包问题只是把“容量”换成了“预算”“价值”换成了“热量”。每种零食只能买一次所以要用一维滚动数组并且内层循环必须从大到小遍历。为什么要倒序遍历因为如果正序遍历dp[j]可能会多次使用同一件零食变成完全背包也就是同一件商品会被买很多次这明显不符合题意。初始化时dp数组全部为0表示预算为j时最大热量。遍历完所有零食后dp[B]就是对总预算B的最优解。注意看样例答案是13而不是14说明这里不是无脑全选要动态取舍。代码实现def solve(): n, B map(int, input().split()) p list(map(int, input().split())) v list(map(int, input().split())) dp [0] * (B 1) for i in range(n): for j in range(B, p[i] - 1, -1): dp[j] max(dp[j], dp[j - p[i]] v[i]) print(dp[B]) if __name__ __main__: solve()这道题对熟练的同学来说几乎是秒杀的但它很能说明美团命题的一个特点DP题不会给你裸的“0/1背包”标题而是用一个团建采购的故事包起来。你第一眼要能识别出这是背包而不是去纠结零食的摆放顺序。笔试现场没有时间让你从头推状态定义平时这些经典模型的转移方程必须印在脑子里。3.4 第四题微服务编译顺序拓扑排序DP题目描述一个项目里有n个微服务编号从1到n。每个微服务有一个编译时间t[i]。服务之间存在依赖关系给定m条关系每条关系用两个整数a b表示含义是“服务a依赖服务b”也就是说服务b必须先编译完成服务a才能开始编译。多个没有依赖关系的服务可以同时编译问最早全部编译完成的时间。输入格式第一行两个整数n和m。 第二行n个整数表示每个服务的编译时间。 接下来m行每行两个整数a b表示a依赖b。输出格式一个整数表示最早全部编译完成的时间。样例输入4 2 3 2 4 1 2 1 3 1样例输出7样例解释服务1编译需要3秒服务4编译需要1秒它们没有依赖关系可以同时开始。服务2依赖服务1所以服务2要到第5秒完成服务3依赖服务1要到第7秒完成。最终全部完成时间是7秒。解题思路依赖关系天然是拓扑排序的模型。我们把依赖关系看成有向边b先编译所以要从b指向a。每个节点的最早完成时间等于它所有前置节点中最晚完成时间加上自己的编译时间。具体做法统计每个节点的入度入度为0的节点可以最开始编译初始化其完成时间就是自己的编译时间。用队列做拓扑排序每次弹出一个节点u遍历它指向的所有节点v尝试用dp[u] t[v]更新dp[v]取较大值然后把v的入度减1减到0时入队。最后所有节点的最大dp值就是答案。如果最终遍历到的节点数不等于n说明存在循环依赖这种情况题目一般不会出但代码里最好判断一下笔试时多一个判断不会扣分反而显得你考虑周全。代码实现from collections import deque def solve(): n, m map(int, input().split()) t [0] list(map(int, input().split())) graph [[] for _ in range(n 1)] indeg [0] * (n 1) for _ in range(m): a, b map(int, input().split()) graph[b].append(a) indeg[a] 1 q deque() dp [0] * (n 1) for i in range(1, n 1): if indeg[i] 0: q.append(i) dp[i] t[i] cnt 0 while q: u q.popleft() cnt 1 for v in graph[u]: dp[v] max(dp[v], dp[u] t[v]) indeg[v] - 1 if indeg[v] 0: q.append(v) if cnt ! n: print(存在循环依赖) else: print(max(dp)) if __name__ __main__: solve()这道题对基础扎实的同学来说不难但它考察了两个容易混淆的点第一边方向不能建反“a依赖b”意味着b指向a而不是a指向b第二拓扑排序的DP更新是“取最大值”因为一个服务要等所有依赖都编译完只有前置全部完成后才能开始所以取最大完成时间。细看这三道题加一道压轴题你会发现美团笔试根本不需要什么超纲的数据结构红黑树、KMP、后缀数组这些基本不出现。它考的就是你对我们常说的“高频基础算法”的掌握到底熟不熟练模拟、排序、二分、贪心、DP、图论。熟练度决定上限。4. ACM模式下必踩的坑4.1 读入与输出稳才是第一位的美团笔试多数场次使用牛客网或赛码网作为在线评测环境题目是标准的ACM模式所有输入输出都要你自己处理。这里有个很反直觉的坑本地测试样例过了交上去却超时很多时候不是算法问题而是读入太慢。如果你在Python里用input()一行一行读数据量小没问题但订单重量、服务数量一旦到10的5次方级别行数变多input()的性能就会拖后腿。我个人的习惯是一开始就统一用sys.stdin.buffer.read().split()一次性把全部数据读进来然后按顺序取。这样既快又不容易因为行数问题出错。输出也一样不要print一次打一行而是把所有结果收集到列表里最后用\n.join(out)一次性打印。这个细节在只有一道题的时候看不出差距但遇到多组测试用例的场景省下的时间足够你多调一个边界。4.2 复杂度预估先学会给题目“算命”拿到一道题先不要急着写代码看一眼数据范围心里应该立刻有一个复杂度预期n 1000O(n^2)可以接受暴力枚举有时候能直接过。n 10^5必须O(n log n)或O(n)二分、排序、双指针是主打。n 10^9几乎不可能是数组模拟多半是数学公式、二分答案或者某种结论题。涉及图且n 10^5别写DFS迭代写法或BFS更安全Python递归还有爆栈风险。这个判断能帮你决定要不要优化。很多同学看到一个“熟悉”的题目就开始写暴力写完才发现复杂度不够再推到重来时间全浪费了。笔试不是做研究是在规定时间内拿最多分所以“预判复杂度”这个习惯一定得养成。这里再给一个快速转译表帮你把题目关键词对应到算法模型题面关键词优先想到的算法模型最小化最大值 / 最大化最小值二分答案分成k组 / 连续子段 / 负载均衡二分答案 贪心检查预算 / 容量 / 选或不选0/1背包或完全背包依赖关系 / 先后顺序 / 并行完成拓扑排序送餐时间 / 排队窗口 / 最早完成排序 模拟 / 贪心最长递增 / 子序列 / 接龙动态规划图上的最短路 / 最小代价Dijkstra / BFS5. 按第三批风格备考的实操建议5.1 题型地图先覆盖再深入根据2023年美团秋招笔试的命题偏好我认为你刷题的时候应该按以下优先级分配精力第一梯队模拟、排序、二分、贪心。这些是前两题的主力每天都要练保持手感。第二梯队动态规划。背包、线性DP、区间DP这三个方向足够应对美团笔试的大部分DP题重点不是学多少花活而是把基本模型练熟。第三梯队图论。拓扑排序、BFS/DFS、单源最短路。不用研究太深的网络流、最大流笔试基本用不到。字符串、数学、计算几何这些属于冷门时间不够可以放一放。优先级拉满的同学考前一个月按这个清单过一遍效果比盲目刷200道题强得多。5.2 模板沉淀把常用写法练成肌肉记忆笔试现场没有时间让你现场推板子。下面这些模板建议写到本地笔记里并且每天手敲一遍直到完全不用思考二分答案两种边界写法的完整代码0/1背包的一维滚动数组模板完全背包模板内层正序拓扑排序的队列实现含判环Dijkstra的堆优化模板并查集模板路径压缩按秩合并我见过太多同学平时用IDE的代码补全用惯了到笔试的在线编辑器里连二分边界都写不利索。这不是能力不够而是手熟度不够。笔试拼的就是在有限时间内把模板准确调出来、改一改、套进去。这些模板练到肌肉记忆能帮你省下大量思考时间。5.3 关于AI编程工具的两句实话现在AI编程工具确实很火Cursor、各种AI刷题助手、AI提示词玩法层出不穷我自己平时写工程代码也会用。但关于笔试我要说句实在话在线笔试基本是限时手写你是没法依赖AI帮你生成完整代码的。AI编程工具的真正价值是当你的私教。你做完一道题可以让它帮你讲思路、分析复杂度、甚至生成几道同类变体题来练。这个过程能让你更快理解一类题型的底层逻辑比自己闷头背题效率高不少。但临考前三天一定要强制自己脱离AI纯手写所有模板和代码。否则你可能会出现一种错觉看AI写题全会自己一写全废。另外工作里很重要的异步编程、CompletableFuture、并发编程这些内容笔试编程题基本不会直接考。它们更多出现在面试的拷打环节或者实际工程里。备考笔试阶段别把精力放错地方。6. 常见问题速查与避坑清单最后整理一份笔试过程中我见过的高频问题和对应的处理方案你可以直接截图存下来。现象原因解决办法大数据量输入超时用input()逐行读改用sys.stdin.buffer.read().split()第一题做20分钟还过不了没有先按到达时间排序配对后排序用一个cur变量模拟窗口二分答案死循环边界条件混乱统一用left right check收缩的模板DP结果偏大或偏小背包遍历顺序错了0/1背包倒序完全背包正序写前先想清楚拓扑排序结果不对依赖边方向建反把“a依赖b”写成 graph[b].append(a)样例过了提交却WA边界没考虑比如n1或空数组每题完成后用最小规模样例自测最后10分钟才发现第一题有bug检查时间没留够每完成一题立即自测不要攒到最后递归写DFS爆栈Python递归深度限制改用栈模拟或BFS如果非要说这场笔试给我最大的感受那一定是“读题速度本身也是一种能力”。很多人不是不会做而是被题目里的业务场景绕晕了花五分钟才看懂它到底要算什么。我的做法是读题先不看故事直接看输入输出格式和样例基本就能猜出算法模型再回头确认题意。这个习惯帮你省下的时间可能比多刷一百道题还管用。
返回列表