大厂真题 / 美团
美团 2026-9-5 笔试真题 - 算法岗与研发岗
公式说明:数学公式保留为原始矢量图,避免抓取转换造成变量和约束缺失。
美团2026-9-5笔试真题 - 算法岗&研发岗
两场题解整理在一起,希望能帮助大家更好地准备后续笔试。
本场考试概述
考试时间 :2026-9-5
考试岗位 :算法岗 + 研发岗
难度评级 :中等偏难
考点分析 :
选择题(10 道,算法岗):算法数据结构 4 道(递归求值、图的深度优先搜索复杂度、二叉树遍历还原、堆的调整),深度学习 3 道(卷积神经网络、激活函数、词向量),大模型 2 道(分布式训练通信优化、长序列注意力),机器学习 1 道(强化学习价值函数)。
第一题(算法岗):转盘配色——中位数贪心(难度困难)。
第二题(研发岗):等距跳跃——奇偶性判定 + 构造(难度困难)。
建议策略 :
选择题里第 1、6、9、10 题都得动笔算,尤其第 10 题要老老实实把建堆过程走一遍再做中序遍历,光看选项排除不掉;第 5 题是本场最容易想反的一道,FlashAttention 只优化显存与访存、不改变 的复杂度,它才是”无法直接解决长序列”的那一项。
两道编程题都建议先把结论推完再写代码。转盘配色的突破口是”相邻交换换不了同色元素之间的先后次序”,于是第
个
的去处被定死,整道题只剩一个整体旋转量要挑,落到数轴上就是经典的中位数最小化绝对值和;容易漏的是两种交错形态都要各算一遍,只试一种会把本身已交错的串算成非零。
等距跳跃反过来,难点全在无解判定:
为偶数时
必须是偶数,终点是原点时
必须是偶数,两条判据缺一条就会在对应数据上挂掉。构造反而好办,来回跳同一条向量即可。
选择题(10道)
1、货位承重函数写成如下递归。求g(g(g(1))) 的值。
int g(int t) {
return ((t>0)?t*g(t-1):2);
}
A. 2
B. 8
C. 48
D. 36
答案 :C
难度 :简单
考点 :算法—递归
解释 :函数的递归式是 时返回
,
时返回基例
,因此闭式解为
。由内向外逐层计算:
;
;
,选 C。A 的
是只算完最内层
就停手,把中间结果当成了最终答案。D 的
对应不上任何一层的取值,是凑出来的干扰项。B 的
是把基例的
误当成每层的固定返回值连乘三次,忽略了每层还要乘上
。
2、货架俯拍要用卷积网络判断零件是否到位。下列关于卷积神经网络的说法,正确的是?
A. 卷积核越大,网络一定越不容易过拟合
B. 卷积神经网络对图像进行分类,属于有监督学习方式
C. 池化层的作用是把全连接替换成循环神经网络
D. 卷积神经网络不能处理二维输入,只能处理一维序列
答案 :B
难度 :入门
考点 :深度学习—卷积神经网络
解释 :图像分类要靠成对的”图像 + 类别标签”训练,损失函数由预测类别与真实标签之差给出,这正是有监督学习的定义,故 B 正确。A 错, 卷积核的参数量随
增长,核越大参数越多、拟合能力越强,反而更容易过拟合,况且”一定”这种绝对表述在正则化、数据量等条件未定时不成立。C 错,池化是对特征图做下采样,用来压缩空间分辨率、扩大感受野并带来一定的平移不变性,和把全连接换成循环网络毫无关系。D 错,二维卷积本来就是为图像这类二维网格数据设计的,一维序列用的才是 Conv1d,这句话把两者说反了。
3、点云分割大模型用数据并行训练。反向里梯度聚合通信占了 70%,且部分梯度通信可以和后续反向计算重叠。下列哪项优化最直接? A. 按梯度就绪顺序发起通信,使梯度聚合与反向计算重叠 B. 先把优化器换成随机猜参数,不再做反向传播 C. 未看切分与拓扑就改成流水线并行,并关掉所有通信 D. 把每张卡的 batch 扩到显存上限,使通信占比更高 答案 :A 难度 :中等 考点 :大模型—分布式训练 解释 :题干已经给出两个关键前提,通信占比高且通信可与后续反向计算重叠,那么最直接的做法就是让通信别再等反向算完才发起。反向传播是从输出层往输入层走的,靠近输出的层梯度先就绪,把它们按就绪顺序分桶立即发 all-reduce,通信时间就被藏进了尚未算完的前面几层的计算里,这正是 PyTorch DDP 的梯度分桶重叠策略,故选 A。B 错,随机猜参数等于放弃训练,模型根本不会收敛,谈不上优化。C 错,关掉所有通信各卡的参数就不再同步,训出的是若干个互不相干的模型,而且在没看清切分方式与卡间拓扑前改并行策略是盲目动手。D 错,它把目标写成让通信占比更高,与降低通信开销的方向正相反,且把 batch 顶到显存上限还会带来 OOM 风险。
4、巡检照片要判”有裂缝 / 无裂缝”(二分类)。若输出层仅含一个神经元,激活函数通常使用?
A. 线性恒等映射,把输出限制在整数编号上
B. 把所有通道做全局平均后再取 argmax
C. logistic
D. 用随机符号函数代替可导激活
答案 :C
难度 :入门
考点 :深度学习—激活函数
解释 :单神经元二分类要输出的是”有裂缝”的概率,logistic 函数(即 sigmoid) 把任意实数压到
区间,正好可以解释为概率并与二元交叉熵损失配套使用,故选 C。A 错,线性恒等映射的输出范围是整个实数轴,无法当作概率,而且分类任务的输出不该被强行限制成整数编号。B 错,单个神经元只有一个标量输出,没有多个通道可供 argmax,且 argmax 处处不可导,放在输出层会切断梯度回传。D 错,符号函数是阶跃型的,除跳变点外导数恒为
,加上随机化也改变不了梯度消失的事实,网络无法训练。
5、值班日志超长,要用能扛长序列的架构。下列哪种架构设计无法直接解决长序列建模问题?
A. 分段递归缓存历史隐状态,跨段传递长期依赖
B. FlashAttention 的显存优化策略
C. 用核方法把注意力近似成线性复杂度
D. 对远程 token 做分块稀疏连接,使复杂度低于二次
答案 :B
难度 :中等
考点 :大模型—注意力机制
解释 :这题问的是哪一项无法直接解决长序列建模,判据是看它有没有改变注意力的计算复杂度或有效上下文长度。FlashAttention 用分块与在线 softmax 做 IO 感知的计算,避免把 的注意力矩阵写回显存,但它在数学上与标准注意力完全等价,时间复杂度仍是
,属于工程层面的显存与访存优化,故 B 是本题要选的项。A 是 Transformer-XL 的分段递归,把上一段的隐状态缓存下来供当前段使用,有效上下文随段数累积,直接延长了可建模的依赖距离。C 是 Performer 一类线性注意力,用核函数近似 softmax 后把复杂度降到
。D 是 Longformer、BigBird 采用的分块稀疏注意力,只保留局部窗口与少量全局连接,把复杂度压到亚二次。后三者都从复杂度或依赖长度上正面解决问题,只有 B 不改变这两点。
6、现有一稠密图 ,用邻接矩阵存储,深度优先搜索遍历全部顶点。时间复杂度和空间复杂度分别为?(存储图的空间不计入空间复杂度,
为顶点数,
为边数。)
A.
B.
C.
D.
**
答案** :A
难度 :简单
考点 :算法—图论/深度优先搜索
解释 :邻接矩阵下要找出某个顶点的全部邻居,必须扫描矩阵中对应的一整行,代价是
,而 DFS 会对每个顶点各做一次这样的扫描,总时间为
。空间方面题目已说明不计存图开销,剩下的只有标记数组
和递归调用栈,后者在最坏情况下(图退化成一条链)深度为
,仍是
,故选 A。B 的
是 Dijkstra 或 Kruskal 这类带堆或排序的算法的复杂度,DFS 不做任何排序。C 把时间写成
,连读一遍每个顶点的邻接行都不够,邻接表存储下才可能达到
。D 的
时间连访问完所有顶点都做不到,直接排除。
7、巡检机器人按策略 在厂区里走。以下是强化学习中状态价值函数
的定义的是?
A.
B.
C.
D.
**
答案** :C
难度 :简单
考点 :机器学习—强化学习
解释 :状态价值函数的定义是从状态
出发、之后一直遵循策略
所能获得的累积折扣回报
的期望,写作
,故选 C。A 是状态转移概率,描述的是环境动力学,属于 MDP 的模型部分,与回报无关。B 是对动作价值取最大,它等于最优状态价值
,只在最优策略或贪心策略下成立,对任意策略
应当写成按
加权的求和而非取最大。D 是策略在状态
下对所有动作的概率求和,由归一化条件它恒等于
,不含任何价值信息。
8、制度条款要做成词向量。以下关于词汇表征方法的表述正确的是?
A. 每个词只保留一个比特标记,因此 one-hot 一定比稠密向量更省显存
B. word2vec 本质上是一种降维操作
C. 词频统计不能得到任何向量,必须先训练生成式语言模型
D. 字符级卷积会把词表膨胀成与句子长度无关的固定三维坐标
答案 :B
难度 :简单
考点 :深度学习—词向量
解释 :word2vec 的输入是 维的 one-hot,经输入层到隐层的权重矩阵
映射到几百维的稠密空间,而这个矩阵的每一行就是对应词的词向量,整个过程是把高维稀疏表示压缩为低维稠密表示的降维,故 B 正确。A 错,one-hot 是一个
维向量而不是一个比特,词表动辄几万到几十万维,比
维的稠密向量占用大得多。C 错,词袋、TF-IDF 直接由词频统计得到向量,共现矩阵做 SVD 分解就是 LSA,全都不需要生成式语言模型。D 错,字符级卷积的作用恰恰相反,它以字符为基本单元来构造词表示,从而缓解未登录词问题、避免词表膨胀,也不存在什么固定三维坐标的说法。
9、某二叉树前序遍历序列为 R‑A‑D‑B‑E‑C‑F,不可能的中序遍历是?
A. B‑D‑A‑R‑F‑E‑C
B. A‑R‑D‑B‑E‑C‑F
C. D‑B‑A‑R‑E‑F‑C
D. A‑D‑B‑R‑E‑C‑F
答案 :A
难度 :中等
考点 :算法—二叉树遍历
解释 :判定方法是用前序的首元素定根,在中序里以根为界切出左右两段,再递归检查左右段的元素集合与前序中对应位置的元素集合是否一致。前序首元素 R 是根,选项 A 的中序中 R 左边是 、右边是
,与前序中紧随其后的 A、D、B 和 E、C、F 恰好逐段同集合,第一层通过。矛盾出在右子树:右子树前序为 E、C、F 定根 E,而中序 F、E、C 中 E 左边只有 F,说明左子树只含 F,那么前序中紧跟 E 的应当是 F,实际却是 C,无法构造出这样的树,故 A 不可能。B 对应一条不断向右延伸的右斜链,C 与 D 用同样的递归切分逐层验证均可还原出合法的二叉树,三者都是可能的中序序列。
10、将序列 建成完全二叉树,再调整为最小堆后,堆所对应的中序遍历序列可能为?
A. 15, 76, 23, 41, 20, 61, 30, 31, 28, 71
B. 20, 23, 28, 41, 61, 31, 71, 76, 15, 30
C. 76, 23, 41, 20, 61, 30, 15, 31, 28, 71
D. 71, 28, 31, 15, 30, 61, 20, 41, 23, 76
答案 :C
难度 :中等
考点 :算法—堆
解释 :先自底向上做筛选建堆,下标从
计。
处
与子结点
交换;
处
与较小的子结点
交换;
处
小于两个子结点
和
,不动;
处
与
交换后继续下沉但已满足堆序;
处
与
交换并下沉到位。最终数组为
。再按完全二叉树的形态做中序遍历,左子树部分依次访问
,然后是根
,右子树部分是
,拼起来正是选项 C。A 把根
放在了首位,那是前序而非中序的特征,中序中根必然出现在左子树全部结点之后。B 是原始输入序列,完全没有做堆调整。D 的首元素
在堆中是右子树的结点,不可能出现在中序序列的最前面。
第一题(算法岗):转盘配色
题目描述
装配线上有一只圆形转盘,沿圆周均布 个卡槽。每个卡槽里已经放好一块色片,黑色记为
,白色记为
,两种色片恰好各
块。机械臂只能交换相邻两个卡槽里的色片;转盘是圆的,编号为
的卡槽与编号为
的卡槽也算相邻。
质检要求沿圆周看过去必须黑白严格交错,也就是任意两个相邻卡槽颜色都不同。色片只能靠相邻交换挪动,不能取出或替换。
请计算:最少交换多少次,才能让转盘变成严格交错。
输入描述
第一行一个整数 ,表示每种颜色的块数。
第二行一个长度为
的字符串
,只含字符
和
,表示沿圆周依次写下的色片颜色。保证
与
的个数都是
。
输出描述
输出一个整数,表示最少相邻交换次数。
样例1
输入
2
1100
输出
1
样例解释
交换中间相邻的 与
,得到
,已经黑白交错,操作
次。
样例2
输入
3
110100
输出
1
样例解释
首尾两个卡槽相邻。把位置 的
与位置
的
交换,得到
,操作
次。
样例3
输入
1
10
输出
0
样例解释 两个卡槽已经不同色,无需交换。
题解:中位数贪心
题目问题拆解
长度为 的环形
串,
与
各
个,每次可以交换环上相邻的两位,问最少交换多少次能让整圈黑白交错。
这是一道把交换翻译成位移的贪心题:难点不在实现,而在看出每个
该去哪一格由它的序号定死,整道题最后只剩一个整体旋转量要挑。
算法实现
先看一次交换做了什么。同色交换等于没换,有意义的只有异色交换,它恰好让一个 越过一个
。所以
之间的先后次序永远不变,总交换次数等于各个
位移量之和。
再看终点。长度
的环只有两种交错形态:
全落在偶数位,或全落在奇数位。次序既然不变,第
个
只能去第
个目标格。
目标格还差一个自由度:环是闭合的,整圈可以再统一转若干格。记
为第
个
当前的下标,形态
下它的目标格是
,令
,总代价即
式中
是整体旋转量,
是第
个
自己的偏差,两者之差的绝对值就是它要走的格数。
于是问题变成在数轴上挑一个点
,让它到
个已知点
的距离和最小。最优点取在
的中位数:把候选点从中位数往右挪一格,左侧各项各增加
、右侧各项各减少
,一旦左侧点数多于右侧,总量就转为上升。
只能落在偶数上,取不超过中位数的最大偶数、再左右各试一格即可,不必枚举所有
。
两种形态都要算。本身已交错的串只在其中一种形态下代价为
,只试一种就可能把样例
这类输入算成非零。
时空复杂度分析
时间复杂度 : 。瓶颈是对
排序,两种形态各排一次;扫出
的下标、取中位数与三次代价评估都是
。
空间复杂度 :
。存
的下标数组与偏差数组
。
Python
# 转盘配色 - 中位数贪心
def cost_at(c, x):
"""把 c 里的每个数都挪到同一个偶数点 x 上,返回总挪动量"""
total = 0
for v in c:
total += abs(x - v)
return total
def min_swaps(m, d):
"""求让长度 2m 的环形 01 串变成严格交错所需的最少相邻交换次数"""
# 相邻交换换不了 0 之间的先后次序,所以只需记下每个 0 现在站在哪
pos = []
for i, ch in enumerate(d):
if ch == '0':
pos.append(i)
best = None
# 长度 2m 的环只有两种交错形态:0 全在偶数位(s=0)或全在奇数位(s=1)
for s in (0, 1):
# 第 i 个 0 本该落在 2i+s,c[i] 就是它离目标还差多少
c = [2 * i + s - pos[i] for i in range(m)]
# 环是闭合的,整圈还能再统一转 k 格,于是代价变成 sum|2k - c[i]|
c.sort()
mid = c[m // 2]
# 上式对 k 是凸函数,实数最优就在中位数上,取离它最近的偶数点即可
even = mid - ((mid % 2) + 2) % 2
for x in (even - 2, even, even + 2):
cur = cost_at(c, x)
if best is None or cur < best:
best = cur
return best
# 第一行是半长 m,第二行是绕圈写下的色片串
m = int(input())
d = input().strip()
print(min_swaps(m, d))
第二题(研发岗):等距跳跃
题目描述
你站在二维平面整数点 。你希望恰好进行
次跳跃到达整数点
,并且每次跳跃的欧式距离都完全相同且不为
。
请给出每一次跳跃结束后到达的整数点坐标序列,共
个点,最后一个应为
。若无解,请输出
。
说明:欧式距离指两点间的直线距离,从
跳到
的长度为
。
输入描述
每个测试文件包含多组测试数据。第一行输入一个整数 表示测试组数。
随后每组数据一行输入三个整数
。
保证所有测试中
的总和不超过
。
输出描述
对于每组测试数据:
若无解,输出一行 。
若有解,先输出一行
,随后输出
行,每行两个整数,用一个空格隔开,依次为第
次跳跃结束后到达的点坐标
,要求
,
。
坐标范围约束:若存在可行解,所输出的每次跳跃结束坐标
必须满足
。各步的落点可重复,无需保证互不相同。如果存在多个解决方案,输出任意一个即可,系统会自动判定是否正确。
样例1
输入
4
1 0 1
1 0 2
0 0 3
1 1 4
输出
YES
1 0
NO
NO
YES
1 0
1 1
0 1
1 1
样例解释
第一组:一步直接跳到 ,跳跃距离为
,非零,合法。
第二组:两次跳跃长度相同,意味着落脚点必须落在原点与
连线的中垂线
上,该直线不含整数点,故无解。
第三组:终点就是起点,而跳跃次数为奇数,无法用奇数条等长向量首尾相接回到出发点,故无解。
第四组:四次跳跃依次为
,长度都是
,向量之和为
,落点序列即为输出。
样例2
输入
2
0 0 2
3 5 3
输出
YES
1 0
0 0
YES
3 5
0 0
3 5
样例解释
第一组:先跳到 再跳回原点,两次跳跃长度都是
。
第二组:先跳到
,再原路返回原点,最后再跳回
,三次跳跃长度都是
。
题解:奇偶性判定 + 构造
题目问题拆解
从原点出发恰好走 步落到
,每步都是同一个非零长度的整点向量,要么给出一组落点,要么判无解。
这是一道存在性比构造更难的题:能走通时方案随手就能凑出来,难点全在证明什么时候走不通,而无解的判据有两条,漏掉任何一条都会在原点或奇偶不匹配的数据上挂掉。
算法实现
先找无解的必要条件。设每步长度的平方为 ,对任意整点向量
有
,于是
。把
条向量相加得
为偶数时右侧恒为偶数,所以
是奇数就无解。
为奇数时
可以自己挑,这条不构成限制。
终点是原点时还有第二条。设
为奇数且
条等长向量之和为零向量。
为奇数时上式给出
,与
矛盾;
时每个向量的两个分量都是奇数,
是
个奇数之和仍为奇数,矛盾;
时两个分量只能同为偶数,把所有向量整体除以
后仍是同一个问题,而
变成
。
严格变小且恒正,无穷递降后必然落进前两种矛盾。所以原点配奇数步无解。
其余情形都能构造,只有三种形态。
终点非原点、
为奇数时,在终点与原点之间来回,落点依次是
。每跳都是
,长度自然相同,奇数步正好停在
。
终点是原点、
为偶数时同理,改在
与原点之间来回。
终点非原点、
为偶数时,先把
拆成等长的两段
与
。
同为偶数时取
;同为奇数时绕
度取半
两种取法都有
,等长且非零。先走
再走
抵达
,之后每两步做一次退回
再走回
,用的还是同一个长度。
三种形态的落点坐标都不超过
,离题面要求的
还有一倍余量。
时空复杂度分析
时间复杂度 : 。每组只做常数次奇偶判断,之后输出多少个点就花多少时间,瓶颈是输出本身而非计算。
空间复杂度 :
,不计输出。三种形态都是边算边写,不需要额外存下整个序列。
Java
// 等距跳跃 - 奇偶性判定 + 构造
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
public class Main {
// 把 k 个落点依次写进 out;无解返回 false。坐标最大到 10^9,全程用 long
private static boolean jumpPoints(long x, long y, int k, PrintWriter out) {
// 终点就是起点:只能靠来回抵消,奇数步凑不出零向量
if (x == 0 && y == 0) {
if (k % 2 == 1) {
return false;
}
out.println("YES");
// 在 (1,0) 与原点之间来回,两种跳跃长度都是 1
for (int i = 0; i < k; i++) {
out.println(i % 2 == 0 ? "1 0" : "0 0");
}
return true;
}
// 终点非原点且步数为奇数:终点与原点之间来回,最后一步正好停在终点
if (k % 2 == 1) {
out.println("YES");
for (int i = 0; i < k; i++) {
if (i % 2 == 0) {
out.println(x + " " + y);
} else {
out.println("0 0");
}
}
return true;
}
// 步数为偶数时,x+y 必须是偶数,理由见题解里的奇偶不变量
if (((x + y) % 2 + 2) % 2 != 0) {
return false;
}
// 把终点拆成等长的两段 A 与 B,两个分量同奇偶时才存在整点拆法
long ax;
long ay;
if ((x % 2 + 2) % 2 == 0) {
ax = x / 2;
ay = y / 2;
} else {
// x、y 同为奇数时绕 45 度取半,两段长度平方都是 (x*x+y*y)/2
ax = (x + y) / 2;
ay = (y - x) / 2;
}
// B 是 P 减去 A 剩下的那一段,与 A 等长
long bx = x - ax;
long by = y - ay;
out.println("YES");
out.println(ax + " " + ay);
out.println(x + " " + y);
// 剩下的偶数步两两一组:退回 B 再走回终点,用的还是同一个长度
for (int i = 0; i < k / 2 - 1; i++) {
out.println(bx + " " + by);
out.println(x + " " + y);
}
return true;
}
public static void main(String[] args) throws Exception {
// 组数可达 10^5、落点总量可达 5*10^5,读用切词器、写用带缓冲的 PrintWriter
StreamTokenizer in = new StreamTokenizer(
new BufferedReader(new InputStreamReader(System.in)));
PrintWriter out = new PrintWriter(System.out);
// nval 是 double,但 10^9 远在 2^53 内,转 long 不丢精度
in.nextToken();
int t = (int) in.nval;
while (t-- > 0) {
in.nextToken();
long x = (long) in.nval;
in.nextToken();
long y = (long) in.nval;
in.nextToken();
int k = (int) in.nval;
if (!jumpPoints(x, y, k, out)) {
out.println("NO");
}
}
// PrintWriter 带缓冲,不 flush 会丢掉尾部输出
out.flush();
}
}