☰
美团校招笔试算法题备考指南:考点分布与解题模板
2026/9/27 15:42:05 网站建设 项目流程

1. 美团校招笔试到底在考什么

1.1 从2023校招看题型分布

美团2023校招笔试的编程题部分,整体上延续了互联网大厂算法题的一贯风格:以数据结构与算法为核心,覆盖数组、字符串、链表、二叉树、动态规划、贪心、二分、图论这几个主流方向。和字节、阿里、腾讯相比,美团的题目难度不算最顶,但胜在题量大、时间紧、业务场景代入感强,审题稍有偏差就很容易被带偏。

从实际考题分布来看,笔试一般是2到4道编程题,限时60到90分钟。题目难度呈阶梯状:第一题通常是基础送分题,以模拟、字符串处理、简单排序为主,目标是让人人都能拿分;第二、三题开始进入中等难度,动规、贪心、二分、递归这些核心算法轮番上阵;最后一题往往带点区分度,要么是状态压缩DP,要么是带有优化要求的图论题,用来筛选真正有算法功底的人。

一个有意思的细节是,美团的题目经常会把业务场景包装进题干,比如外卖配送、骑手调度、商家评分、用户偏好。但剥掉这层外壳之后,核心考点反而更朴素,无非还是那些经典的算法模型。所以备考的核心思路很明确:不要被题干的故事性迷惑,先抽象出数学模型,再用熟悉的数据结构和算法框架去套。

1.2 算法题的分值占比与过线策略

从通过率倒推,美团笔试的算法编程题分值占比通常在60%到80%之间。这意味着即使前面的选择题或主观题答得不理想,只要算法题能稳稳拿下前两道,进入面试阶段的概率依然很大。反过来,如果编程题大面积空着,其他部分再强也难补回来。

我建议的策略是“保二争三冲四”。前两道题必须在30分钟内解决,这是底线。第三题是拉开差距的关键,能做对一半就有竞争力。第四题不必死磕,实在没有思路就输出暴力解或多写几个if分支去逼近部分分值,千万不要在一道题上卡满全场。

还有一个容易忽略的点:美团的初筛不只是看分数,还会参考代码风格和提交记录。如果你前两题都是压着超时线提交,后面又频繁报错,就算最后分数凑合,面试官也可能对你的代码功底打一个问号。所以平时练习就要养成好习惯:先写思路注释,再写代码,最后检查边界。

1.3 题目难度梯度判断

拿到题目后的第一件事不是马上敲代码,而是花30秒判断题目梯度。我习惯用三个信号来判断:

  • 看数据范围:n ≤ 10的,大概率是暴力枚举或全排列;n ≤ 10^5的,基本要求O(n log n)甚至O(n);n ≤ 10^9的,基本可以确定需要数学推导或二分答案。
  • 看考点关键词:见到“最多”“最少”“最长”优先想贪心和动态规划;见到“是否存在”“第k个”优先想二分答案;见到“所有路径”“连通性”优先想DFS、BFS或并查集。
  • 看题目场景:外卖配送类多和最短路径、区间调度有关;商家评分类多和排序、TopK、哈希统计有关;红包补贴类多和贪心、背包、DP沾边。

这种判断能力刷题刷多了自然会有。关键是带着这种意识去做题,而不是一道接一道地无脑刷。

2. 考前必备:ACM模式与常用模板

2.1 为什么必须练ACM模式

美团笔试用的是牛客网这类在线评测系统,代码要自己写输入输出,也就是俗称的“ACM模式”。这和平时在LeetCode上直接补全函数体完全是两回事。很多刷惯了LeetCode的同学,笔试时反而会挂在最简单的地方——不会处理输入。

我记得有次模拟笔试,一道题要求从一行读入多个整数,有同学直接写input().split(),但题目实际给的数据是用逗号分隔的,他完全没注意到,结果样例都过不了。类似这种细节,只有靠平时多练ACM模式才能踩平。

建议备考期间,所有题目都用标准输入输出方式写一遍。别嫌麻烦,这是笔试的基本功。

2.2 快读快写模板

Python写输入输出特别容易踩性能坑,尤其是处理大输入时,input()和print()的耗时会被成倍放大。我每次笔试都会先写好一套快读快写模板,直接复用:

import sys def solve(): # 读入一行多个整数 n, m = map(int, sys.stdin.readline().split()) # 读入一个n行二维数组 grid = [list(map(int, sys.stdin.readline().split())) for _ in range(n)] # 输出结果 sys.stdout.write(str(ans)) if __name__ == "__main__": solve()

用sys.stdin.readline()替换input(),用sys.stdout.write()替换print(),在大数据量下能省下不少时间。另外,如果你拿不准一行里有多少个数据,或者数据跨行分布,可以用sys.stdin.buffer.read().split()一次性读取全部token再解析,稳定又省心。

2.3 高频数据结构模板:二分、拓扑排序、并查集

准备笔试不能只背API,还要背模板。常见的几个模板我必须提醒你提前整理好,考场上直接默写就行。

二分模板是重中之重,尤其是边界条件。我常用的是左闭右闭区间的写法:

def check(mid): # 自定义判断条件 pass l, r = 0, 10**18 ans = -1 while l <= r: mid = (l + r) // 2 if check(mid): ans = mid l = mid + 1 else: r = mid - 1

这套模板的优势在于,最后ans保持的是最后一个满足条件的解,不会出现死循环或越界问题。贪心+二分、DP+二分都可以直接套。

并查集模板同样高频,几乎每个考图论的场次都会出现:

parent = list(range(n)) def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x def union(a, b): ra, rb = find(a), find(b) if ra != rb: parent[ra] = rb

这里的路径压缩用了“隔代压缩”,写起来简单,实际速度也不差。如果遇到带权并查集,那是在union里加一个权值数组来维护节点到根的距离,美团笔试里也出现过,值得专门练一练。

3. 真题复盘:一道完整题目的解题链路

3.1 2023实战题:外卖骑手的订单调度

我拿一道我复盘过的2023真题来讲,题目大概意思是:

小美是外卖站点的调度员,现在有n个订单,每个订单有一个最晚送达时间t_i,以及制作需要的时间d_i。骑手一次只能配送一单,配送期间不能中断。问小美最多能完成多少订单?

这种题一出来,第一反应可能是按截止时间排个序,然后一个个完成。但这只是基础思路,并不一定最优。比如一个耗时很长的订单可能会堵住后面若干短订单的路。我们要的是一种能动态调整的贪心策略。

3.2 从暴力到贪心的思维过程

暴力方法很简单:枚举所有订单子集,检查是否存在一个执行顺序,使得每个订单都能在截止时间前完成。但n一上10,复杂度直接爆炸,肯定不行。

真正的解法是贪心+优先队列。

核心思想:

  1. 所有订单按截止时间t_i从小到大排序。
  2. 用一个最大堆维护“已选订单的加工时长”。
  3. 遍历每一个订单,把它加入计划,同时累加总耗时cur。
  4. 如果cur超过了当前订单的截止时间,就从堆里弹出一个加工时长最大的订单,把它从计划中移除,同时cur减去对应时长。
  5. 最终堆的大小就是最多能完成的订单数。

为什么这么贪是对的?因为按截止时间排序后,当前的每个订单都是一个“新的最紧迫项”。如果执行不完,说明总时长超出了,这时候为了让完成数量尽量多,就应该踢掉耗时最长、价值却相同的那个订单。堆在O(log n)时间内维护这个最大值,整体复杂度O(n log n)。

3.3 完整AC代码与复杂度分析

import sys import heapq def solve(): n = int(sys.stdin.readline()) orders = [] for _ in range(n): t, d = map(int, sys.stdin.readline().split()) orders.append((t, d)) # 按截止时间升序 orders.sort(key=lambda x: x[0]) heap = [] cur = 0 for t, d in orders: heapq.heappush(heap, -d) cur += d if cur > t: cur += heapq.heappop(heap) # 注意heap存的是负数 print(len(heap)) if __name__ == "__main__": solve()

这里有一个容易踩的坑:Python的heapq默认是小根堆,所以存-d来模拟最大堆。弹出时heapq.heappop(heap)返回的是-d,加到cur上相当于减去d。我见过不少同学在这里符号搞反,样例第一组能过,第二组就崩了。

时间复杂度的细节也要说清楚:排序是O(n log n),每个订单最多入堆和出堆各一次,每次O(log n),所以整体是O(n log n)。空间复杂度O(n),也就是堆的容量。

如果美团把数据范围放到n最大10^5,这个解法是完全没有压力的。

4. 高频考点拆解:DP、贪心、图论与字符串

4.1 动态规划的两种常考模型

美团笔试里的动态规划,主要以线性DP和区间DP为主。线性DP最常见的是背包类问题:0-1背包、完全背包、分组背包。题干翻来覆去,可能是商家补贴的组合方案、优惠券的叠加规则,但本质都是“选或不选”的价值最大化问题。

以0-1背包为例,状态转移就三行:

dp = [0] * (V + 1) for i in range(n): for v in range(V, weight[i] - 1, -1): dp[v] = max(dp[v], dp[v - weight[i]] + value[i])

注意内层循环必须从大到小遍历,否则同一个物品会被重复装入,变成完全背包。这个细节我至少见过十次笔试翻车现场。

区间DP在美团笔试里出得少一点,但一出来就是压轴难度。典型特征是“在数组上做合并/切分,求最大最小代价”。比如石子合并、括号匹配、字符串编辑距离。写区间DP时先枚举区间长度,再枚举左端点,最后枚举分割点,模板很固定:

dp = [[0] * n for _ in range(n)] for length in range(2, n + 1): for i in range(n - length + 1): j = i + length - 1 for k in range(i, j): dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + cost(i, j, k))

模板归模板,难点在cost函数的定义。那需要结合题意去推,没有一键搞定的技巧。

4.2 贪心+优先队列的经典套路

贪心在美团笔试里出现的频率相当高,而且很少单独考,基本都是和优先队列搭配,像上文那道订单调度题就是典型。这个套路可以总结成一句话:按一个维度排序,用另一个维度做堆内比较。

具体说,就是先按“截止时间”“分数”“利润”这类约束条件排序,然后用优先队列维护“当前已选集合中的某种属性极值”。遇到新元素时,如果候选集合的总和或合法性超过了约束,就弹出最大的那个。这类题的变形非常多,像日程安排、任务调度、区间选取,底层都是同一套思路。

练习时要关注贪心选择性质的证明,面试官可能会追问“为什么这个贪心是对的”。虽然笔试只要求写代码,但在复盘时把证明过程写一遍,后续面试时才会游刃有余。

4.3 图论与搜索的优先级排序

图论题在美团笔试里主要以搜索题形式出现。最基础的是BFS和DFS,用来解决连通块数量、最短路径、拓扑排序等问题。送外卖的题十有八九会把地图抽象成二维网格,这时候BFS就是唯一正解。

BFS模板要背得滚瓜烂熟:

from collections import deque def bfs(start, end, grid): n, m = len(grid), len(grid[0]) dist = [[-1] * m for _ in range(n)] dist[start[0]][start[1]] = 0 q = deque([start]) while q: x, y = q.popleft() if (x, y) == end: return dist[x][y] for dx, dy in ((1,0),(-1,0),(0,1),(0,-1)): nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < m and grid[nx][ny] != -1 and dist[nx][ny] == -1: dist[nx][ny] = dist[x][y] + 1 q.append((nx, ny)) return -1

特别提醒:dist数组初始化为-1,既保存了距离,又充当了visited数组,节省一份空间。这个技巧很实用。

如果图不是网格,而是普通的邻接表图,那Dijkstra也得会。美团偶尔会在最后一题考带权最短路,比如带时间窗约束的配送路径问题。这时候堆优化的Dijkstra是标准解法,注意把“节点+当前时间”作为状态,否则会漏掉窗口约束。

4.4 字符串考点:KMP与排序思想的应用

字符串题在美团笔试里不算大宗,但一旦出现就很容易卡人。最典型的是KMP算法,考察点是next数组的理解和构造。这里有一个经典考题:对模式串"abacaba"求next数组。这种题在笔试中考察频率非常高。

我们直接推导一遍,方便理解:

  • next[i]表示模式串前i个字符组成的子串中,最长相等前后缀的长度。具体到"abacaba":
  • i=0时,next[0]=-1(或者0,取决于教材定义,这里用-1起始)。
  • i=1时,子串"a",没有真前后缀,next[1]=0。
  • i=2时,子串"ab",最长相等前后缀长度为0,next[2]=0。
  • i=3时,子串"aba",前缀"a"等于后缀"a",next[3]=1。
  • i=4时,子串"abac",没有,next[4]=0。
  • i=5时,子串"abaca",前缀"a"等于后缀"a",next[5]=1。
  • i=6时,子串"abacab",前缀"ab"等于后缀"ab",next[6]=2。
  • i=7时,子串"abacaba",前缀"aba"等于后缀"aba",next[7]=3。

KMP的构建代码很简洁:

def build_next(p): m = len(p) nxt = [0] * m j = 0 for i in range(1, m): while j > 0 and p[i] != p[j]: j = nxt[j - 1] if p[i] == p[j]: j += 1 nxt[i] = j return nxt

如果时间不够,字符串题可以适当放一放。相比DP和图论,它的出题密度低不少。但基础概念和模板至少要能默写。

5. 提交前必须做的排查:常见错误与边界

5.1 五类常见编译/运行错误

很多人笔试失败不是不会做,而是败在低级的运行错误上。我总结五类最容易踩的坑:

第一是数组越界。习惯性开dp[n][m],但状态转移时访问了dp[i-1],当i=0的时候就崩了。对策是统一从1开始编号,或者在转移前加if判断。

第二是除零错误。求平均、求比例时,分母可能为0,尤其当输入数据包含空集合时。写代码前先想清楚数据范围里有没有0出现。

第三是死循环。while循环里忘记更新循环变量,或者二分判断条件写反,都会导致TLE。建议在本地调试时直接给极端数据测试。

第四是栈溢出。深递归在Python里尤其危险,默认递归深度只有1000。如果DFS的递归深度可能超过这个值,赶紧改用BFS或手写栈。

第五是输入读取错误。一行里面有多个空格、换行符,或者数据是逗号分隔、分号分隔,都可能让你读到的数据错位。这一步读错,后面全白搭。

5.2 边界条件自测清单

我每次提交前都会用一组边界数据自测一下。这里分享我固定的测试清单:

  • 当n=0或n=1时,程序是否正常返回?
  • 当所有数字都相同时,答案是什么?
  • 当结果是极大值或极小值时,是否溢出?
  • 当输入含负数时,排序、比较逻辑是否还成立?
  • 当数组长度为奇数/偶数时,二分或中位数是否受影响?

这些测试不是浪费时间,而是用一分钟成本避免一次提交失败。尤其是在ACM模式只显示部分用例通过时,边界自测能帮你快速定位问题。

5.3 性能超时后的优化顺序

如果提交后显示超时,别急着乱改,按优先级检查:

第一,检查输入输出方式。换用sys.stdin.buffer.read()和sys.stdout.write()后,很多超时问题直接消失。

第二,检查算法复杂度。如果双层循环不可避免,想想能不能用哈希表把内层循环降下来。比如查找某个元素是否存在,用set代替遍历列表,立刻从O(n)变成O(1)。

第三,检查重复计算。用一维DP代替二维DP,用记忆化递归代替暴力回溯,都是常见的降复杂度手段。

第四,检查常数级优化。把len()调用提到循环外,把属性访问缓存到局部变量,这些细致优化在临界超时时能救你一命。

6. 刷题路径与心态调整:我的个人心得

如果现在离笔试还有一个月,我建议按这个节奏来:第一个星期集中刷数据结构和排序、二分、双指针;第二个星期集中刷贪心和动态规划;第三个星期刷图论和字符串,同时穿插综合模拟;最后一周每天一套完整真题,严格按照考试时间限时完成。

刷题打卡不是比数量,比的是复盘质量。每道题做完,我都会在文档里记录三件事:考点是什么、我的思路卡在哪、最优解的巧妙之处在哪里。等到笔试前一晚,不看代码,只看这些记录,效果比临时抱佛脚强得多。

至于心态,我见过太多同学因为第一题卡太久,后面全崩了。一定要记住,笔试是策略游戏,不是英雄主义。确保会做的全对,再谈挑战难题。美团笔试的题量虽然大,但只要稳住节奏,前一小时把该拿的分拿到手,最后一题就算只写个暴力解,结果通常也不会差。

最后再分享一个小经验:平时刷题尽量用英文变量命名,函数名用max_profit、calc_distance这种可读性强的风格。美团笔试出结果后,面试官能看到你的代码,清晰整洁的代码风格会成为隐形的加分项。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询