大厂真题 / 华为
华为 7.15 笔试真题 - AI岗
本场考试概述
考试时间:2026年7月15日 考试岗位:AI岗(国内) 难度评级:中等偏难
考点分析:
- 选择题(20道):概率与参数估计、线性变换、数值误差、评价指标、t-SNE、度量学习、KNN、层次聚类、GELU、LN/RMSNorm、优化器、Transformer Pre-LN、SFT、注意力 Mask、向量检索、Token Merging
- 第一题:KV Cache 稀疏化管理器——最小堆 + 精确小数比较 + 有序插入字典(难度中等)
- 第二题:Agent 执行轨迹压缩——轨迹模拟 + 状态压缩 BFS(难度困难)
建议策略:
- 选择题主攻概率统计与经典机器学习概念,大模型部分重点留意注意力、向量检索和 Token 压缩等工程知识
- 第一题把需求拆成两条线:最小堆负责找淘汰项,按位置递增插入的字典负责按位置输出
- 第二题先模拟出最终网格,再只保留非零格子;目标格不超过 12 个,提示用二进制掩码记录访问状态
选择题(20道)
一、单选题
1、在回归任务中,我们想要评估模型预测值与真实值之间的误差。如果数据中存在少量极端异常值(Outliers),以下哪个指标对异常值最敏感?
A. 平均绝对误差(MAE)
B. 中位数绝对误差
C. 均方根误差(RMSE)
D. 绝对百分比误差(MAPE)
答案:C
难度:简单
考点:机器学习—评价指标
解析:RMSE 先对误差取平方再求平均,平方运算会把大误差急剧放大,因此对极端异常值最敏感。MAE 与 MAPE 是一次项、对异常值线性响应;中位数绝对误差取中位数、几乎不受少量离群点影响,稳健性最强。
2、某单词在垃圾邮件中出现的概率是 $0.8$,在正常邮件中出现的概率是 $0.1$。已知邮箱中垃圾邮件占 $20\%$。如果一封邮件包含该单词,它是垃圾邮件的概率是?
A. $0.5$
B. $0.8$
C. $0.2$
D. $0.667$
答案:D
难度:简单
考点:数学—概率论/贝叶斯
解析:由贝叶斯公式,
分子是垃圾邮件且含该词的概率,分母是含该词的总概率。
3、设样本 $X_1,X_2,\ldots,X_n$ 来自泊松分布 $P(\lambda)$,则 $\lambda$ 的最大似然估计量与样本方差 $S^2$ 的关系为( )
A. $E[X]\ne\lambda,\ E[S^2]=\lambda$
B. $E[X]\ne\lambda,\ E[S^2]\ne\lambda$
C. $E[X]=\lambda,\ E[S^2]\ne\lambda$
D. $E[X]=E[S^2]=\lambda$
答案:D
难度:中等
考点:数学—概率论/参数估计
解析:泊松分布的均值与方差都等于 $\lambda$。$\lambda$ 的最大似然估计量是样本均值 $\hat\lambda=\bar X$,满足 $E[\bar X]=\lambda$;若 $S^2$ 表示分母为 $n-1$ 的无偏样本方差,则 $E[S^2]=\operatorname{Var}(X)=\lambda$。故两者期望都等于 $\lambda$。
原题记号说明:原文选项写作 $E[X]$,而题干询问的是 $\lambda$ 的最大似然估计量,结合原文解析应将这里的 $X$ 理解为 $\hat\lambda=\bar X$。
4、某团队用 t-SNE 将 $10$ 万条 $768$ 维向量降维至 $2$ 维进行可视化,发现聚类边界清晰,但降维后的 $2$ 维向量直接输入 LightGBM 训练下游分类任务,准确率显著低于原始 $768$ 维。以下说法正确的是?
A. 可视化与训练任务的目标冲突,应分别用 t-SNE(2D)和 PCA(50D)各降维一次
B. t-SNE 降维后的低维特征更适合模型训练,应检查 LightGBM 参数设置
C. t-SNE 计算复杂度 $O(n^2)$,$10$ 万样本规模下应改用 UMAP
D. t-SNE 保留局部邻域结构但破坏全局距离,$2$ 维坐标无绝对意义,不适合特征工程输入
答案:D
难度:中等
考点:机器学习—降维
解析:t-SNE 优化的是局部邻域的相似性,坐标轴与全局距离都没有稳定含义,只适合可视化,不能当作特征喂给下游模型。D 抓住了本质原因;A、C 是转移话题的干扰项,B 与现象矛盾。
5、在 $M_{2\times2}$($2\times2$ 矩阵空间)中,映射 $T(A)=A^T$(转置)是线性的吗?
A. 否,因为转置改变了元素位置
B. 否,因为 $(AB)^T=B^TA^T$
C. 是
D. 否,因为转置不可逆
答案:C
难度:入门
考点:数学—线性代数/线性变换
解析:转置满足 $(A+B)^T=A^T+B^T$ 与 $(cA)^T=cA^T$,同时满足可加性与齐次性,因此是线性映射。B 说的乘法转置规律与“是否线性”无关,D 中转置其实可逆(自身为逆),均为干扰项。
6、为实现高维类别数据的线性嵌入,要求嵌入后的向量满足“同类向量相似度高,异类向量相似度低”,下列线性嵌入方案中最合理的是?
A. 采用独热编码,将每个类别映射到高维稀疏向量,再通过随机投影得到低维向量
B. 基于类别标签的监督学习,优化线性变换矩阵,使同类向量的欧氏距离最小化、异类向量的欧氏距离最大化
C. 采用随机生成的变换矩阵,将所有类别映射到同一低维空间
D. 忽略类别信息,将所有离散数据随机映射到低维向量空间
答案:B
难度:中等
考点:机器学习—度量学习/嵌入
解析:目标是“同类近、异类远”,本质是有监督的度量学习,需要用类别标签优化线性变换矩阵。B 正是这一思路。A、C、D 都使用随机映射或忽略标签,无法保证类内聚合、类间分离。
7、以下哪种数据格式最适合直接用于 SFT 训练?
A. {"prompt": "法国的首都是哪里?", "chosen": "巴黎", "rejected": "伦敦"}
B. {"instruction": "法国的首都是哪里?", "output": "巴黎"}
C. {"question": "法国的首都", "answer": "巴黎", "score": 0.95}
D. {"text": "巴黎是法国的首都,位于塞纳河畔。"}
答案:B
难度:简单
考点:大模型—微调(SFT)
解析:SFT(监督微调)需要“指令—回答”成对数据,B 的 instruction/output 正好对应。A 含 chosen/rejected,是偏好数据(用于 DPO);C 带 score,更像奖励模型数据;D 是无标注纯文本(用于预训练)。
8、在训练 decoder-only 语言模型并使用右侧 padding 时,attention mask 的正确处理通常是?
A. 同时使用 causal mask 与 padding mask,避免看未来和看到无 token
B. 仅使用 causal mask 即可,padding token 会自动忽略
C. 仅使用 padding mask 即可,因为标签处会被忽略
D. 不需要 mask,交给 loss 的 ignore_index 处理
答案:A
难度:中等
考点:大模型—注意力机制
解析:decoder-only 模型需要 causal mask,保证每个位置只能看到自己及之前的 token;padding mask 保证注意力不落到无意义的填充位。二者叠加是通用且稳健的做法。仅靠 loss 的 ignore_index 只能屏蔽损失,不能阻止注意力读取填充位。
9、某班 $60\%$ 是男生,$40\%$ 是女生。男生中有 $25\%$ 喜欢编程,女生中有 $50\%$ 喜欢编程。随机选一个喜欢编程的学生,是男生的概率是?
A. $\dfrac{3}{7}\approx42.9\%$
B. $60\%$
C. $25\%$
D. $\dfrac{1}{2}$
答案:A
难度:简单
考点:数学—概率论/贝叶斯
解析:男生且喜欢编程的概率为 $0.6\times0.25=0.15$,女生且喜欢编程的概率为 $0.4\times0.5=0.2$,喜欢编程的总概率为 $0.35$。故
10、在 Transformer 中,Residual Connection(残差连接)和 Layer Norm 通常有两种组合方式:Post-LN(原版论文)和 Pre-LN(现代大模型主流)。Pre-LN 的优势是:
A. Pre-LN 将 Layer Norm 置于残差路径内,梯度在主路径上直接流通,训练更稳定,更易于扩展到深层网络
B. Pre-LN 要求更大的 batch size 才能稳定训练
C. Pre-LN 的参数量少于 Post-LN
D. Pre-LN 去掉了残差连接,简化了模型结构
答案:A
难度:中等
考点:深度学习—Transformer
解析:Pre-LN 把归一化放在子层输入端,残差主干上保留一条不经过 LN 的“直通”路径,梯度可无衰减地回传,因此深层训练更稳定、往往无需复杂的学习率 warmup。B、C、D 与 Pre-LN 的机制无关或说法错误。
11、GELU(Gaussian Error Linear Unit)是 Transformer 模型(如 BERT、GPT)中常用的激活函数。关于 GELU,下列说法正确的是?
A. 它是 ReLU 的精确线性近似
B. 它在负区间完全截断为 $0$
C. 它的计算复杂度远高于 Swish
D. 它期望根据输入的随机性决定是否激活,是一种平滑的 ReLU 近似
答案:D
难度:简单
考点:深度学习—激活函数
解析:GELU 用输入乘以标准正态的累积分布,即 $x\Phi(x)$,可以理解为“按输入大小的概率决定是否保留”,是一条平滑曲线。A 错在“精确线性”,B 错在负区间并非硬截断(仍有小的非零输出),C 与实际计算特性不符。
12、关于误差的传播,下列说法正确的是:
A. 误差在任何运算中都不会被放大
B. 乘法运算中绝对误差等于各项绝对误差之积
C. 加法运算中绝对误差等于各项绝对误差之和
D. 加法运算中相对误差等于各项相对误差之和
答案:C
难度:中等
考点:数学—数值计算/误差分析
解析:按原题对最坏情况误差限的表述选择 C。严格地说,带符号误差满足 $\Delta z=\Delta a+\Delta b$,因此
只有误差同号时取等号;C 应理解为“和的绝对误差上界等于各项绝对误差上界之和”。A 错误,相近数相减会放大相对误差;B 错误,乘法绝对误差的一阶近似为 $b\Delta a+a\Delta b$,不是绝对误差之积;D 错误,相对误差相加是乘法误差限的近似规律,不是加法的规律。
13、在 Agent 的长期记忆机制中,向量检索(Vector Retrieval)是常用的技术。假设长期记忆库中有 $N$ 个记忆片段,每个片段被编码为 $D$ 维向量。当 Agent 需要检索与当前查询 $Q$ 最相关的 $k$ 个记忆时,需要计算查询向量与所有记忆向量的相似度。关于时间复杂度的说法中,正确的是:
A. 检索时间复杂度为 $O(N\times D)$,因为需要计算查询向量与所有 $N$ 个 $D$ 维向量的点积
B. 检索时间复杂度为 $O(\log N)$,因为使用向量索引结构近似最近邻搜索
C. 检索时间复杂度为 $O(k)$,因为只需要返回 $k$ 个结果
D. 检索时间复杂度为 $O(N)$,因为需要遍历所有记忆片段
答案:A
难度:简单
考点:大模型—向量检索
解析:题干明确采用“计算查询向量与所有记忆向量相似度”的暴力检索。每次点积耗时 $O(D)$,共 $N$ 个向量,故总复杂度为 $O(N\times D)$。D 漏掉了每个向量的 $D$ 维计算量;B 是近似索引的复杂度,与题干设定不符;C 只考虑了返回结果数。
14、考虑到视觉特征中的空间冗余性,部分模型采用 Token Merging(ToMe)策略进行压缩,该策略的核心思想是?
A. 使用卷积神经网络的步长(Stride)直接下采样
B. 基于二分图匹配,在 Transformer 层内部将相似度较高的视觉 Token 进行合并
C. 随机丢弃 $50\%$ 的视觉 Token
D. 通过自回归模型预测并保留重要的 Token
答案:B
难度:困难
考点:大模型—多模态/Token压缩
解析:ToMe 在每个 Transformer 层内把 token 分成两组做二分图软匹配,将最相似的若干对 token 合并,从而无需训练即可逐层压缩序列长度。A 是卷积下采样,C 是随机丢弃,D 是自回归预测,都不是 ToMe 的做法。
15、下列哪个是线性变换的性质?
A. $T$ 保持向量夹角不变
B. $T(v)=v^2$
C. $T(-v)=-T(v)$
D. $T$ 保持向量长度不变
答案:C
难度:入门
考点:数学—线性代数/线性变换
解析:线性变换满足齐次性 $T(cv)=cT(v)$,取 $c=-1$ 即得 $T(-v)=-T(v)$,故 C 正确。保持夹角、保持长度是正交变换的性质(线性变换的特例),并非一般线性变换都成立;B 含平方项,是非线性的。
二、多选题
16、关于优化器选择与训练现象,下列判断更合理的是哪些?
A. 所有任务最终都应收敛到同一种优化器选择
B. 只要训练损失低,模型泛化一定更好
C. 在稀疏梯度场景中,Adam 类方法通常更有优势
D. 更换优化器时,往往需要联动调整学习率等超参数
答案:C、D
难度:中等
考点:深度学习—优化器
解析:
- A 错误:不同任务、数据分布与模型结构对优化器的偏好不同,没有“万能唯一”的选择。
- B 错误:训练损失低可能是过拟合,泛化能力应由验证集或测试集表现衡量。
- C 正确:Adam 等自适应方法按参数维护学习率,在稀疏梯度(如 NLP 词嵌入)下更新更充分。
- D 正确:不同优化器的有效步长尺度不同,更换优化器通常要重新调整学习率等超参数。
17、下列哪些是层次聚类(Hierarchical Clustering)中常用的簇间距离度量(Linkage)方法?
A. Average Linkage
B. Single Linkage
C. Ward Linkage
D. Complete Linkage
答案:A、B、C、D
难度:简单
考点:机器学习—聚类
解析:
- Average Linkage 使用两簇所有点对距离的平均值。
- Single Linkage 使用两簇最近点对的距离,容易形成链状簇。
- Ward Linkage 选择使簇内平方和增量最小的合并,倾向于得到大小均衡的簇。
- Complete Linkage 使用两簇最远点对的距离,倾向于得到紧凑的球状簇。
四者都是层次聚类的标准 linkage 准则。
18、关于 Transformer 中的归一化操作,以下哪些说法是正确的?
A. 归一化有助于加速模型训练收敛
B. 层归一化(LN)和批量归一化(BN)的作用相同,可将经典 Transformer 中的 LN 直接替换为 BN
C. RMSNorm 是层归一化的一种简化实现,只重新缩放不进行平移
D. 层归一化(LN)是对每个样本的特征进行归一化
答案:A、C、D
难度:中等
考点:深度学习—归一化
解析:
- A 正确:归一化可以稳定各层输入分布,缓解梯度问题并加速收敛。
- B 错误:BN 依赖 batch 统计量,对变长序列与小 batch 不稳定,不能直接替换 LN。
- C 正确:RMSNorm 去掉均值中心化,只使用均方根重新缩放,不进行平移,是 LN 的简化。
- D 正确:LN 在单个样本的特征维度上做归一化,与 batch 无关。
19、在使用 K 近邻算法进行样本归组时,以下哪些问题是常见的挑战?
A. 高维空间中近邻关系可能变得不稳定
B. 不同 K 取值可能导致分组结果差异较大
C. 对异常点和局部噪声较敏感
D. 大规模数据下近邻搜索开销较高
答案:A、B、C、D
难度:简单
考点:机器学习—KNN
解析:
- A 正确:维数灾难下各点距离趋于接近,近邻判定不稳定。
- B 正确:$K$ 太小容易受噪声影响,太大则可能跨类别平滑,分组结果对 $K$ 敏感。
- C 正确:KNN 直接依赖邻居标签,异常点与局部噪声会直接干扰判定。
- D 正确:暴力检索复杂度为 $O(N\times D)$,大规模数据下搜索开销高,需要借助 KD-Tree、近似最近邻等方法加速。
20、关于递推算法的数值稳定性,以下说法正确的有:
A. 若递推中误差传播系数的绝对值大于 $1$,则正向递推不稳定
B. 选择合适的递推方向是保证数值稳定性的关键
C. 所有递推算法都是数值不稳定的
D. 不稳定的正向递推有时可以通过反向递推来解决
答案:A、B、D
难度:中等
考点:数学—数值计算/递推稳定性
解析:
- A 正确:传播系数绝对值大于 $1$ 时,误差会随迭代逐步放大,正向递推不稳定。
- B 正确:同一问题正向、反向递推的误差放大特性可能相反,选择合适的方向是关键。
- C 错误:稳定性取决于误差放大系数,很多递推算法是数值稳定的,不能一概而论。
- D 正确:正向递推不稳定时,改用反向递推有时能让误差随迭代衰减。
第 1 题:KV Cache 稀疏化管理器
题目描述
在大语言模型(LLM)的自回归推理中,KV Cache 用于缓存历史 token 的 Key 和 Value 张量,其显存占用随序列长度线性增长。为支持长上下文,系统设定容量上限 $K$,仅保留注意力分数最高的 $K$ 个 token。
需要支持两种操作:
ADD pos score:插入一个新 token 的 KV 对及其注意力分数。插入后若缓存数量超过 $K$,立即淘汰分数最低的 token;若最低分并列,则淘汰位置编号pos最小的 token。QUERY:按位置编号pos从小到大输出当前缓存中的所有 token 及其 KV。
输入格式
第一行包含两个整数 $K,N$,分别表示容量上限和操作数量,其中 $1\le K,N\le10^5$。
接下来有 $N$ 个操作:
ADD操作占三行。第一行是ADD pos score,其中 $0\le pos\le10^9$,pos唯一且随插入顺序严格递增,$0.0\le score\le1000.0$;第二行是 Key 向量的 $4$ 个浮点数;第三行是 Value 向量的 $4$ 个浮点数。QUERY操作占一行,仅包含QUERY。
输出格式
对每个 ADD 操作,如果触发淘汰,输出一行 PRUNED pos;未触发则不输出。
对每个 QUERY 操作,先输出当前缓存数量 $M$,然后按 pos 升序输出 $M$ 组数据。每组占三行,依次为 pos score、Key 向量和 Value 向量。分数与向量必须按照插入时的文本原样输出。
样例
输入
3 7
ADD 0 5.0
1.0 2.0 3.0 4.0
0.1 0.2 0.3 0.4
ADD 1 2.0
2.0 2.0 2.0 2.0
0.5 0.5 0.5 0.5
ADD 2 8.0
3.0 3.0 3.0 3.0
0.9 0.9 0.9 0.9
QUERY
ADD 3 6.0
4.0 4.0 4.0 4.0
0.0 0.0 0.0 0.0
QUERY
ADD 4 9.0
5.0 5.0 5.0 5.0
1.0 1.0 1.0 1.0
输出
3
0 5.0
1.0 2.0 3.0 4.0
0.1 0.2 0.3 0.4
1 2.0
2.0 2.0 2.0 2.0
0.5 0.5 0.5 0.5
2 8.0
3.0 3.0 3.0 3.0
0.9 0.9 0.9 0.9
PRUNED 1
3
0 5.0
1.0 2.0 3.0 4.0
0.1 0.2 0.3 0.4
2 8.0
3.0 3.0 3.0 3.0
0.9 0.9 0.9 0.9
3 6.0
4.0 4.0 4.0 4.0
0.0 0.0 0.0 0.0
PRUNED 0
思路分析
第一步:把查询和淘汰拆成两个排序维度
QUERY 要按 pos 递增输出,而淘汰要按 (score, pos) 递增选择。一个数据结构很难同时按两套关键字高效工作,因此分别维护缓存字典与最小堆。
第二步:用最小堆定位淘汰项
最小堆存放 (score, pos)。Python 元组按第一项、第二项依次比较,所以堆顶恰好是分数最低且并列时 pos 最小的 token。分数使用 Decimal(score_text) 解析,避免二进制浮点舍入影响极接近分数的先后关系;用于输出的分数、Key 和 Value 则保留原始文本。
第三步:利用严格递增的 pos 保持查询顺序
题目保证 pos 唯一且随插入顺序严格递增。Python 3 的字典保持插入顺序,删除元素不会改变其余元素的相对顺序,因此直接遍历缓存字典就是按 pos 升序,无需在每次 QUERY 时重新排序。
第四步:维护同步不变量
每次插入同时写入字典和最小堆;超出容量时,从堆顶弹出同一个 token,并从字典删除。这样堆和缓存始终保存同一批 token,缓存数量不会超过 $K$。
以样例为例,前三个分数为 $5.0,2.0,8.0$,缓存刚好装满。插入分数 $6.0$ 后,堆顶 (2.0,1) 被淘汰;再插入 $9.0$ 后,新的堆顶 (5.0,0) 被淘汰。
题解代码
import heapq
import sys
from decimal import Decimal
input = sys.stdin.readline
capacity, operation_count = map(int, input().split())
cache = {}
min_heap = []
write = sys.stdout.write
for _ in range(operation_count):
command = input().split()
if command[0] == "ADD":
pos = int(command[1])
score_text = command[2]
key_text = input().rstrip("\r\n")
value_text = input().rstrip("\r\n")
cache[pos] = (score_text, key_text, value_text)
heapq.heappush(min_heap, (Decimal(score_text), pos))
if len(cache) > capacity:
_, pruned_pos = heapq.heappop(min_heap)
del cache[pruned_pos]
write(f"PRUNED {pruned_pos}\n")
else:
query_output = [str(len(cache))]
for pos, (score_text, key_text, value_text) in cache.items():
query_output.append(f"{pos} {score_text}")
query_output.append(key_text)
query_output.append(value_text)
write("\n".join(query_output) + "\n")
复杂度分析
设一次查询时缓存中有 $M$ 个 token,所有查询实际输出的 token 总数为 $S$。
时间复杂度:每次 ADD 和淘汰均为 $O(\log K)$,每次 QUERY 为 $O(M)$,总时间复杂度为 $O(N\log K+S)$。
空间复杂度:字典、最小堆与单次查询的输出缓冲区均为 $O(K)$。
第 2 题:Agent 执行轨迹压缩
题目描述
一个 Agent 在 $n\times m$ 的二维网格上移动并执行操作。$n$ 和 $m$ 均为奇数,Agent 从网格正中心出发。每个时间步先移动、再操作,两件事合起来算作一步。
移动指令为 U、D、L、R。如果移动后仍在网格内,Agent 到达新位置;如果移动越界,Agent 停留在原地,但仍会执行本步操作。
操作指令为:
I x:将当前格子的状态值增加 $x$;D x:将当前格子的状态值减少 $x$;N 0:不进行操作。
所有格子的初始状态均为 $0$。给定一条完整原轨迹,需要构造一条最短的语义等价轨迹,使新轨迹执行后的每个格子状态与原轨迹完全相同,起点仍是网格中心,终点也与原轨迹终点相同。输出最短轨迹长度。
构造语义说明:输入中的原轨迹满足
I/D参数 $1\le x\le100$;本题来源题解对“构造出的新轨迹”采用的语义是I/D参数可以取任意正整数,因此到达一个非零目标格一次,就能用一次操作写入其最终净值。本文按这一语义求解。若新轨迹同样被限制为 $x\le100$,则还必须记录每个格子剩余的操作量,下面的状态压缩模型不再成立。
输入格式
第一行包含两个正整数 $n,m$,满足 $3\le n,m\le15$,并且 $n,m$ 均为奇数。
第二行包含一个正整数 $K$,表示原轨迹长度,满足 $1\le K\le2000$。
接下来 $K$ 行,每行包含 move op param:
move是U、D、L、R之一;op是I、D、N之一;- 当
op为I或D时,$1\le param\le100$;当op为N时,param固定为 $0$。
数据保证:原轨迹执行后,状态非零的格子不超过 $12$ 个。
输出格式
输出一个整数,表示最短语义等价轨迹的时间步数。
样例
输入
3 3
5
U I 1
U I 2
R D 1
D I 3
R I 1
输出
3
原轨迹执行后的网格为:
0 3 -1
0 0 4
0 0 0
非零格为 $(0,1)=3$、$(0,2)=-1$、$(1,2)=4$,终点为 $(1,2)$。从中心 $(1,1)$ 出发,依次向上、向右、向下,正好用三步访问三个目标格并停在原终点。
输入
5 5
6
R I 2
R D 1
U I 3
L I 1
D D 2
R I 4
输出
4
原轨迹执行后的网格为:
0 0 0 0 0
0 0 0 1 3
0 0 0 0 3
0 0 0 0 0
0 0 0 0 0
非零格为 $(1,3)=1$、$(1,4)=3$、$(2,4)=3$,终点为 $(2,4)$。一条最短轨迹是先从中心向上做无操作,再向右、向右、向下依次写入三个目标值,共四步。
最短轨迹可能不唯一,只需输出最短长度。初始时刻不能直接操作起点,必须先执行一次移动后才能对落点操作。
思路分析
第一步:模拟原轨迹,得到真正需要复现的结果
从中心开始依次执行 $K$ 条指令。每一步先尝试移动;若越界就保留原位置;随后把 I 或 D 造成的净变化累加到当前位置。模拟结束后得到原终点 end,以及每个格子的最终净值。
第二步:把数值问题转成访问目标格问题
在本文采用的构造语义下,新轨迹的操作参数没有上限。一个格子最终为正,就在第一次到达时执行一次 I;最终为负,就执行一次 D。因此只需要访问所有最终非零的格子,具体净值不必进入搜索状态。
把这些非零格子记为目标集合 $T$。题目保证 $\lvert T\rvert\le12$,可以给每个目标分配一个二进制位,用 mask 表示已经到达过哪些目标。
第三步:定义状态并使用 BFS
搜索状态是 (当前位置, mask)。每次向四邻格移动一步;如果落到第 $i$ 个目标格,就执行对应操作并更新:
所有边的代价都是一步,所以使用 BFS。第一次到达 (end, full_mask) 时,当前层数就是最短轨迹长度。
第四步:处理起点不能直接操作
BFS 的初始状态是 (center, 0),即使中心本身是非零目标,也不会在时间 $0$ 自动置位。由于 $n,m\ge3$,中心不是边界,无法用越界移动原地操作;必须先离开中心再走回来,落回中心时才会完成该目标。这个初始化恰好落实了题目的起点约束。
越界移动只会原地消耗一步。对于最短轨迹,它不会比在首次到达边界目标时直接完成操作更优,因此 BFS 只扩展网格内的四邻格。
题解代码
import sys
from collections import deque
input = sys.stdin.readline
n, m = map(int, input().split())
step_count = int(input())
start_row, start_col = n // 2, m // 2
row, col = start_row, start_col
delta = {}
directions = {
"U": (-1, 0),
"D": (1, 0),
"L": (0, -1),
"R": (0, 1),
}
for _ in range(step_count):
move, operation, param_text = input().split()
param = int(param_text)
dr, dc = directions[move]
next_row, next_col = row + dr, col + dc
if 0 <= next_row < n and 0 <= next_col < m:
row, col = next_row, next_col
if operation == "I":
delta[(row, col)] = delta.get((row, col), 0) + param
elif operation == "D":
delta[(row, col)] = delta.get((row, col), 0) - param
end_id = row * m + col
targets = [cell for cell, value in delta.items() if value != 0]
target_index = {cell: index for index, cell in enumerate(targets)}
state_count = 1 << len(targets)
full_mask = state_count - 1
def shortest_path():
start_id = start_row * m + start_col
visited = [bytearray(state_count) for _ in range(n * m)]
visited[start_id][0] = 1
queue = deque([(start_id, 0)])
distance = 0
while queue:
for _ in range(len(queue)):
cell_id, mask = queue.popleft()
if cell_id == end_id and mask == full_mask:
return distance
current_row, current_col = divmod(cell_id, m)
for dr, dc in directions.values():
next_row = current_row + dr
next_col = current_col + dc
if not (0 <= next_row < n and 0 <= next_col < m):
continue
next_mask = mask
bit_index = target_index.get((next_row, next_col))
if bit_index is not None:
next_mask |= 1 << bit_index
next_id = next_row * m + next_col
if not visited[next_id][next_mask]:
visited[next_id][next_mask] = 1
queue.append((next_id, next_mask))
distance += 1
return -1
print(shortest_path())
复杂度分析
设最终非零目标格数量为 $t$,其中 $t\le12$。
时间复杂度:模拟原轨迹为 $O(K)$;BFS 最多访问 $n\times m\times2^t$ 个状态,每个状态扩展四个方向,总复杂度为 $O(K+n m\,2^t)$。最坏扩展次数约为 $225\times4096\times4\approx3.7\times10^6$。
空间复杂度:访问标记和队列最多保存 $O(n m\,2^t)$ 个状态。
小结
- 选择题覆盖概率统计、经典机器学习、Transformer 训练和大模型工程,既考公式也考适用边界
- 第一题的关键是同时维护淘汰顺序与查询顺序;精确比较分数、原样保留文本可以避开浮点格式陷阱
- 第二题先把完整轨迹压缩成不超过 12 个非零目标,再用
(位置, 已访问集合)做 BFS;建模成立的前提是新轨迹单次操作可以写入任意正整数幅度