直接说结论:美团2023校招笔试第1场的编程题,本质上并不是在考什么偏题怪题,而是在考察你能不能搞定“业务里真实会碰到的逻辑问题”。我翻了过去几年美团和其他大厂的笔试题,也和几个上岸的学弟学妹对过思路,这场考试的核心就是四道编程题,覆盖模拟、贪心、动态规划、图论/搜索这几类常规考点,但出题背景几乎都套了一层外卖、商家、骑手、优惠券的业务壳子。如果你正在准备美团的校招笔试,或者想了解大厂笔试到底怎么筛人,这篇内容值得你完整看一遍。
先说下这场笔试的基本盘。美团校招笔试一般是90到120分钟,第1场的题目通常设置成4道编程题,难度从简单到困难递增,每道题20到40分不等。语言不限,C++、Java、Python都可以,核心是ACM模式——也就是你自己处理输入输出,而不是像LeetCode那样只需要补全函数。很多人挂在第一题往往不是不会做,而是不熟悉ACM模式的输入解析,这个我后面专门讲。整场考试下来,四道题的难度分布大概是:前两道属于“认真读题就能AC”的送分题,第三道开始上强度,第四道就是用来拉开差距的压轴题。如果你目标只是过笔试,保住前三题基本就稳了;想冲更高的面试排序,第四题至少要拿一半分数。
1. 内容整体设计与思路拆解
美团的笔试题目风格非常明显:几乎每道题都会设定在一个具体的业务场景里,比如骑手配送、商户评分、优惠券核销、排队取餐。这其实是很多互联网大厂的共同套路,因为笔试不仅仅在考算法,还在看你能否把业务问题抽象成数学模型。美团尤其吃这一套,毕竟外卖、到店、酒旅这些核心业务本身就是海量数据和复杂调度问题堆出来的。
1.1 从业务场景抽象出算法模型的思路
美团笔试中的题面普遍比较长,经常会给一段背景描述,比如“小美是外卖平台的一名骑手,她需要在n个商家之间取餐并配送”,然后才落到具体的输入输出上。这时候如果你被冗长的业务故事带偏了,就很容易抓不住重点。正确做法是快速跳读,直接锁定几个关键信息:数据范围、输入格式、求的是什么量。
以“骑手取送”类型的题目为例,业务壳子再花哨,本质上就是给你一个有向图或一棵树,让你求最短路径、最大收益或可行性判断。商家、骑手、订单这些名词只是变量名的包装。所以平时练习时要有意识做“翻译练习”——看到一段业务描述,能不能在三分钟内说出它对应哪种算法模型。这个能力我建议提前一周开始每天练两题,到考场上就会轻松很多。
1.2 四道题难度梯度与拿分策略
从第1场的整体情况来看,题目的梯度设计是有讲究的。第一题通常考察字符串处理或简单模拟,不需要任何高级数据结构,纯粹看你写的代码能不能正确实现逻辑。第二题一般是贪心或二分答案,难度中等偏易,需要一点算法敏感度,但基本上一眼能看穿。第三题开始上动态规划了,状态转移方程可能不是那么显然,需要你画表推一遍。第四题则是图论搜索或复杂DP的组合题,有时会融合两个以上考点,用来筛选真正有竞赛基础或刷题量足够的人。
针对这个梯度,我的拿分策略是:拿到题先全部扫一遍,别从第一题开始闷头做。前两题争取40分钟内搞定并保证AC,第三题花30分钟推状态和写代码,第四题如果剩下时间不够就写暴力,能跑过小数据也有一半分数。大厂的笔试评测一般不是全对才能得分,很多是部分用例通过就给分,所以暴力解的性价比并不低。
1.3 核心考点分析:为什么美团偏爱这几类题
我个人整理了过去几年美团的笔试真题,发现高频考点集中在四类:贪心、动态规划、图论(尤其是最短路和最小生成树)、以及二分答案。其中贪心和DP出现频率最高,几乎每场必考。原因也好理解:外卖配送的路径规划本质是图论问题,定价策略和优惠券组合本质是DP问题,而骑手调度、订单分配很多场景需要贪心策略做近似最优解。
所以备考时别盲目刷题,优先吃透这几类。我见过太多人把时间花在平衡树、后缀数组这类冷门考点上,结果笔试题全是贪心DP,导致会的不考、考的不会。美团这种综合性大厂的笔试,考的是“基础算法能力是否扎实”,而不是“冷门技巧知道多少”。
2. 第1场编程题的典型题型与解题框架
下面我按题型分类,把第1场笔试中最可能出现的四类题拆开讲。每一类我都会讲清楚题型特征、通用解法、以及易错点。这些内容不是押题,而是帮你建立一套“看到题目就知道往哪个方向想”的反射。
2.1 字符串与模拟题:第一题的稳定送分项
模拟题的特点是无脑但繁琐,考察的是代码实现的准确性。美团喜欢在模拟题里加入一些业务元素,比如“订单号校验”“商家ID格式转换”“评论内容过滤”等。这类题你只需要严格按照题意一步步实现,注意边界条件。
拿“订单号校验”举例,一般会给一串订单号,要求判断是否符合某种规则,比如长度限制、前缀要求、校验位计算。解法就是纯模拟:先判断长度,再逐位检查字符范围,最后按规则计算校验位比对。代码量不大,但最容易翻车的地方在字符串边界处理——比如数组越界、大小写转换、换行符残留。
实操技巧:在ACM模式下读取一行带空格的字符串,建议用getline(cin, s)而不是cin >> s,因为后者遇到空格就停了。如果是Python,sys.stdin.readline().strip()处理多行输入时记得处理末尾换行。这种小细节看似无关痛痒,但考场上真能坑掉一大批人。
2.2 贪心类题目:识别“局部最优即全局最优”的信号
贪心题在美团笔试里非常常见,核心特征是:题目要求最大化或最小化某个值,且所有元素之间没有明显的状态依赖。一旦你能识别出这个信号,就可以尝试用贪心解。常见模型有:区间调度、任务排序、资源分配、最大收益等。
以“优惠券抵扣”为例,假设你有n张优惠券,每张面额不同,买一件商品只能使用一张,问怎样选择才能让实付金额最少。这种题就是典型贪心——选面额最大且不超过商品价格的优惠券即可。实现上可以先对优惠券排序,再用二分查找找到最合适的券。
但贪心题有个大坑:看起来像是贪心,但其实需要动态规划。判断标准是“当前选择是否会影响后续决策”。如果会,那就不是贪心题;如果不会,就放心用。我建议拿到题目先写几个简单用例手算一下,看局部最优能不能推出全局最优,这比直接套模板靠谱得多。
2.3 动态规划类题目:第三题的主力题型
动态规划是美团笔试的重头戏,第1场第三题大概率是DP。常见模型有:背包问题、最长上升子序列、编辑距离、区间DP、状态压缩DP。美团的特点是把DP包装在一个业务场景里,比如“商家满减活动的最优组合”“骑手一天最多能接多少单”。
以“满减组合”为例,你在外卖平台上有一堆满减券,每种券有使用门槛和减免金额,问在总价给定情况下最多能省多少钱。这个模型本质上就是完全背包或01背包的变种——每种券视为物品,门槛和减免额分别对应重量和价值。状态定义可以是dp[i][j]表示前i张券在已选总金额为j时的最大减免额,转移方程就是取或不取当前券的较大值。
DP题最怕的是状态定义不清导致转移方程写不出来。我的建议是拿到题先明确三件事:状态是什么、转移条件是什么、初始化和边界是什么。把这三个问题写在草稿纸上,比直接敲代码有效得多。另外,能用一维数组压缩状态的尽量压缩,省内存的同时代码也更简洁。
2.4 图论与搜索类题目:压轴题的常见形态
第四题难度高,往往是因为它把图论和DP或者贪心混在一起考。比如“外卖骑手需要在多个取餐点之间规划路线,要求经过所有点并返回起点,求最短路径”——这就是典型的旅行商问题简化版,数据范围小的话可以用状态压缩DP解,数据范围大的话就需要剪枝或启发式搜索。
图论题中我建议大家重点掌握三种基本功:Dijkstra最短路、DFS/BFS遍历、以及拓扑排序。美团笔试中涉及图的题,大部分都能用这三种方法解。比如“订单依赖关系判断”就是拓扑排序,“骑手最短送达时间”就是Dijkstra,“地图连通性判断”就是BFS/DFS。这三种算法代码模板不长,考前背熟能够显著提速。
这里插一句,如果第四题完全没思路,别硬编。我见过不少人在最后一道题上死磕,导致前面本来能对的题也来不及检查。合理的时间分配是:第四题如果20分钟内没有明确思路,直接写一个暴力版本,能过多少过多少,然后回头检查前面的题。
3. 实操过程与核心环节实现
光讲理论不够,下面我用三道和第1场笔试风格高度一致的真题重构,完整走一遍从读题到AC的全过程。三道题分别代表简单模拟、贪心、DP三个层次,刚好覆盖笔试的拿分主线。
3.1 真题重构一:订单校验(模拟题完整实现)
题目背景是平台需要对一批订单号做格式校验。订单号由字母和数字组成,长度必须为10,前3位必须是大写字母,中间4位必须是数字,最后3位可以是字母或数字,但最后一位必须是校验字符。校验规则是:前9位字符的ASCII码之和除以26取余,余数对应大写字母(0对应A,1对应B,以此类推),最后一位必须等于这个字母。
这题完全就是模拟,考察细心程度。思路拆开如下:
- 读入n,然后循环n次,每次读入一个订单号。
- 依次检查长度、前3位是否为大写字母、中间4位是否为数字。
- 计算前9位ASCII码之和,取余26得到校验字母。
- 比对最后一位是否等于校验字母,输出结果。
Python实现:
import sys def validate(order: str) -> bool: if len(order) != 10: return False if not order[:3].isupper(): return False if not order[3:7].isdigit(): return False total = sum(ord(c) for c in order[:9]) check = chr(ord('A') + total % 26) return order[-1] == check def main(): data = sys.stdin.read().strip().split() n = int(data[0]) orders = data[1:1+n] for od in orders: print("OK" if validate(od) else "Invalid") if __name__ == "__main__": main()这里有几个关键细节:第一,字符串切片判断大写用isupper(),但isupper()对非字母字符返回False,所以长度校验要在切片前做,否则空串切片会出错。第二,sys.stdin.read()一次性读入所有数据再切分,比逐行input()更快也更稳定,尤其在输入量大的时候。第三,校验字符计算用的是前9位字符的ASCII码之和,注意别把最后一位也算进去了。
3.2 真题重构二:外卖骑手的最优取餐顺序(贪心完整实现)
题目背景:骑手在起点配送站,需要去n个商家取餐,每个商家有一个取餐耗时和一个承诺出餐时间。骑手到达某个商家后,如果还没到出餐时间就需要等待,如果已经出餐就可以直接取走。问怎样安排取餐顺序,使得最后一个商家取完餐的时间最早。
这题初看像调度问题,其实是一个经典贪心——按“承诺出餐时间”升序排列即可。原因在于,等待时间会阻塞后续所有取餐,所以越早承诺出餐的商家越应该优先取,这样可以减少总等待时间。
完整解法思路:
- 将所有商家按出餐时间从小到大排序。
- 维护当前时间cur,从0开始。
- 遍历排序后的商家,cur加上取餐耗时;如果cur小于承诺出餐时间,需要等待到出餐时间再取。
- 最后cur就是完成所有取餐的时间。
C++实现:
#include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; vector<pair<int, int>> shops(n); // {出餐时间, 取餐耗时} for (int i = 0; i < n; i++) { cin >> shops[i].second >> shops[i].first; } sort(shops.begin(), shops.end()); long long cur = 0; for (auto &p : shops) { cur += p.second; if (cur < p.first) cur = p.first; } cout << cur << endl; return 0; }这里为什么要按出餐时间排序而不是按取餐耗时排序,很多新手会想反。我举个实际例子:假设商家A取餐耗时很长但出餐非常快,商家B取餐耗时很短但出餐很慢。如果先取A再取B,取完A后B可能已经出餐了,直接取走不等待;如果先取B再取A,取完B后还要等A出餐很久,总时间反而更长。所以核心矛盾是“等待时间”而不是“取餐耗时”,排序基准应该选出餐时间。
3.3 真题重构三:满减优惠券最优组合(DP完整实现)
题目背景:你有m张满减券,每张券有一个“使用门槛”x和一个“减免金额”y,使用条件是订单总价不低于x。你选购了n件商品,每件商品有价格,你可以选择任意商品组合来凑单,但每个订单只能用一张券。问在所有可能的组合中,实际支付金额最低是多少。
这题的难点在于“选商品组合”和“选券”两个决策耦合在一起。直接思路是对商品价格做子集枚举,然后对每个子集挑选最优的券,但商品数量稍大就爆了。正确的解法是用DP预处理出“凑到某个价格所需的最小商品成本”,然后对每个可能的凑单总价,选择最优的券并计算实付金额。
具体步骤:
- 用01背包预处理:
dp[i]表示能否用已选商品凑出总价i,同时记录凑出i的最小商品数(不是必须的,但有助于理解)。 - 枚举所有可能的凑单总价i,从0到商品总价sum。
- 对每个i,在所有门槛x <= i的券中选减免金额最大的一张。
- 实付金额为i减去减免金额,取全局最小值。
当然,这里有个简化:如果商品价格都是整数且总价不大,背包可行性DP是可行的;如果总价很大,就需要换思路——对所有券按门槛排序,然后只考虑门槛附近的凑单价格。但为了说明DP思路,我先给一个标准解法。
Python实现:
import sys def main(): data = sys.stdin.read().split() idx = 0 n = int(data[idx]); idx += 1 prices = list(map(int, data[idx:idx+n])); idx += n m = int(data[idx]); idx += 1 coupons = [] for _ in range(m): x = int(data[idx]); y = int(data[idx+1]); idx += 2 coupons.append((x, y)) total = sum(prices) dp = [False] * (total + 1) dp[0] = True for p in prices: for j in range(total, p - 1, -1): if dp[j - p]: dp[j] = True ans = total # 最优情况最多等于全部商品原价 for i in range(total + 1): if not dp[i]: continue best_discount = 0 for x, y in coupons: if x <= i: best_discount = max(best_discount, y) ans = min(ans, i - best_discount) print(ans) if __name__ == "__main__": main()这个解法在总价不超过5000时完全可行,复杂度是O(n * total + total * m),在笔试的数据范围内能跑过。但要注意,如果商品数量多到超过30,背包状态会爆炸,这时就需要考虑用“最接近门槛的凑单价”来优化。笔试时先写能过的版本,再考虑优化,这是最务实的策略。
3.4 笔试中的输入输出处理与调试技巧
ACM模式是很多平时只刷LeetCode的考生的噩梦。LeetCode只需要你实现一个函数,输入输出框架都给你搭好了;ACM模式则需要自己处理标准输入输出,处理不好连样例都过不了。
我总结了几条核心经验:
第一,能用sys.stdin.read()就用它,读完后统一split()得到所有token,再按顺序解析。这样能避免多行读取时可能出现的空行和换行符问题。
第二,输出结果时,如果每行一个结果,用'\n'.join()拼接后一次性打印,比循环里逐个print()效率高很多。
第三,写代码前先手动跑一遍题目给的样例,确认输出格式一致。很多题目样例输出最后一行没有换行符,如果你多打了一个空行可能就判错。
第四,如果本地调试没问题但提交后报错,优先检查数组下标是否越界、数据类型是否溢出(比如总金额累加最好用long long)、以及排序比较器是否写反。
4. 常见问题与排查技巧实录
这部分是我这些年带人准备校招笔试,以及自己反复刷题过程中总结出来的高频问题。每个问题都对应一个实实在在的坑,踩中一个就可能丢不少分。
4.1 时间不够用,题目做不完怎么办
美团第1场笔试的时间在90到120分钟,四道题全部认真做完对大多数人来说不现实。我看到很多人的错误做法是第一题死磕30分钟,结果导致后面能拿分的题完全没时间看。正确做法是拿到题后先把四道题全部读一遍,每道题花一两分钟预估难度,然后按“能AC优先、暴力其次、放弃最后”的顺序执行。
具体来说,第一道题和第三道题先做,因为第一道是送分题,第三道是DP题,分值通常较高且思路清晰。第二题贪心放在第三题之前或之后都可以,看具体状态。第四题如果一眼没有思路,果断写暴力,然后立刻回头检查其他题的边界条件。不要因为一道题卡住就心态爆炸,考试比的不是满分,而是相对排名。
4.2 样例能过但提交后0分,问题出在哪
这是笔试中最让人崩溃的情况。常见原因有三个:输入读取错误、边界条件遗漏、数据范围溢出。
输入读取错误最典型的是用input()读取整行字符串但没处理末尾换行,或者用cin >>读取含空格的字符串导致截断。边界条件遗漏则常见于数组长度判断、空数组、重复元素、最小值最大值等情况。数据范围溢出在金额、距离累加时特别常见,Java和C++的int只有32位,一旦累加超过2^31 - 1就会变成负数,结果全错。
我的排查建议是:写一个对本地的随机小数据测试脚本,和暴力解法对比结果。这个习惯能帮你快速定位错误,比盯着代码干想效率高得多。笔试时虽然不能跑随机脚本,但你可以在草稿纸上手动构造几个极端用例——全部最小、全部最大、重复值、空输入——然后用代码逻辑推一遍。
4.3 如何有效刷题,避免陷入题海战术
很多人准备校招笔试时陷入一个误区:疯狂刷LeetCode,一天刷十道,两周刷完三百道,结果笔试照样拉胯。原因在于刷题没有针对性,也不重视总结。美团的笔试风格相对固定,与其漫无目的地刷,不如针对性地练。
我的建议是分三个阶段准备。第一阶段用三天时间过一遍基础数据结构和算法模板,包括排序、二分、DFS、BFS、贪心、背包DP、Dijkstra,每类至少手写一遍模板。第二阶段刷美团近三年的笔试真题和类似风格的模拟题,每天2到3道,重点是每道题做完后总结出题套路和解题突破口。第三阶段进行全真模拟,找牛客网或赛码网的模考环境,限时完成4道题,模拟真实考试节奏。
我在实际刷题过程中发现,很多题你当时看答案觉得懂了,关掉答案自己写还是卡壳。所以每道题一定要关掉题解独立写一遍,写到AC为止,然后再看题解对比思路。这种“先独立AC、再对比优化”的方式,远比反复看题解高效。
4.4 笔试中的心态管理与时间分配技巧
最后聊点看起来跟编程无关、但实际影响很大的东西:考场心态。校招笔试是大家都很紧张的环境,周围人敲键盘的声音、倒计时的时间压力、某一道题卡住时的焦虑感,都会直接影响你的发挥。
我自己的经验是:拿到题先深呼吸一次,用前2分钟把四道题全部看一遍,给每道题标注一个“预期难度”和一个“时间上限”。一旦某道题到达时间上限还没有AC,立刻放下转下一道。不要恋战。这不仅是时间管理,更是一种止损意识——一道题卡30分钟,损失的可能是后面两道的AC分数。
另外,编程笔试允许使用本地IDE,考前一定把自己熟悉的代码模板准备好,比如快读模板、排序模板、二叉树遍历模板、Dijkstra模板。这些模板能让你在考试中节省大量敲代码的时间。但要注意,模板最好是自己理解并手写过的,别指望考场上照抄一份没见过的模板能解决问题。
5. 从第1场笔试延伸出的备考思路
美团第1场笔试的题目风格和难度,基本能代表美团整个校招季的笔试水平。如果你能把这一场的四类题吃透,后面的批次也不会差太多。下面从更深一层聊聊备考思路。
5.1 算法题背后的业务逻辑思维
我在前面提到,美团笔试的题面总是裹着一层厚厚的业务外衣。这层外衣不是无缘无故的,它反映了美团对工程师的一种期望:能从业务问题中抽象出技术方案。外卖订单调度、骑手路径规划、商家满减策略、用户优惠券核销,这些都是美团真实业务中每天都在发生的事。
所以你在准备笔试的时候,不要只盯着算法技巧,也试着思考一下:如果我真的在美团做开发,面对“骑手如何规划路线才能最短时间送达”这个问题,我会怎么设计数据结构和算法?这种思维方式不仅对笔试有帮助,对后续的面试也很有价值。
笔试题目通常只是业务问题的简化版,真实场景要考虑的因素多得多。但从简化的题目出发,去思考它背后的真实业务逻辑,会帮你在面试时面对系统设计题时有更落地的思路。
5.2 如何从笔试复盘反推面试准备
如果你第1场笔试发挥一般,别急着沮丧。笔试之后还有面试,而面试的算法难度通常比笔试低,但更看重你说思路、讲复杂度、和面试官交流的能力。从这个角度看,笔试暴露出的薄弱点反而是你面试前重点准备的方向。
我建议做一份复盘文档,记录每道题的错误原因:是算法不会、边界漏了、还是代码写太慢。针对性地查漏补缺。比如如果你DP老是想不出状态转移方程,面试前就把背包、LIS、编辑距离、区间DP四类题狠狠练一遍,直到看到任意题面都能快速定位到对应模型。
5.3 编程语言选型与备考工具推荐
笔试语言选择上,我推荐用Python或Java,除非你对C++特别熟练。Python写起来最快,字符串处理和列表操作非常省事;Java的模板和类库也很丰富,适合喜欢强类型的人。C++的优势在于执行效率和标准库的算法支持,但写起来确实比Python慢。笔试不是比赛谁代码跑得快,而是比谁能在规定时间内写出正确代码,所以选自己最熟的语言就好。
刷题平台方面,牛客网是校招笔试的首选练习场,因为它完全模拟了企业笔试的ACM输入输出模式,题目也多是历年大厂真题。LeetCode则更适合练算法思维,尤其是动态规划和图论专题。另外赛码网也有不少企业笔试模拟题,可以作为补充。
最后再分享一个我个人的备考习惯:每刷完一道题,在代码注释里用一两句话记录这道题的“题目特征”和“解题突破口”。比如“题目特征:n个商家,每家有出餐时间,求最短完成时间;解题突破口:按出餐时间排序的贪心”。这些备注积累久了,你会在考试时形成快速的模式识别,看到题面就能直接猜到考什么知识点,审题效率大幅提升。
美团2023校招笔试第1场,整体难度在互联网大厂里属于中等偏上,但它非常有代表性——业务包装重、算法模型基础、时间压力大。只要你能把模拟、贪心、DP、图论这四类核心题型练扎实,再把ACM模式的输入输出处理熟练,通过笔试并进入面试是大概率事件。考场上记住一句话:先做能拿分的,再啃硬骨头。把握好这个节奏,你就能在有限时间内拿到最高分数。