大厂真题 / 百度
百度 2026-8-20 笔试真题 - 算法岗
本场考试概述
考试时间:2026年8月20日
考试岗位:算法岗
难度评级:中等偏难
考点分析:
- 第一题:机器学习、深度学习、概率论和基础数据结构选择题
- 第二题:相邻逆序交换模拟与逆序对上界剪枝
- 第三题:对称配对差值求和与奇偶性判断
- 第四题:排序、前缀和与分层贪心
建议策略:
- 第一题的操作次数可能达到 $10^9$,不能直接逐轮模拟;
- 大输入量要使用快速读入;
- 答案可能超过 32 位整数,使用 64 位整数或 Python 整数。
第 1 题:基础知识单选题
本场第一部分为基础知识单选题,覆盖机器学习、深度学习、概率论、分词器和数据结构等方向。下面列出代表性题目与考点。
1. 召回率
某二分类模型共有 40 个真实正类样本,其中检出 30 个。按
\[Recall = \frac{TP}{TP+FN}\]计算,召回率为多少?
答案:$30/40=0.75$。
考点:召回率的分母是真实正类总数;查准率的分母才是预测为正的样本数。
2. SFT 数据中的编造事实
如果 SFT 数据中的 assistant 回复包含用户资料之外的编造事实,最直接的训练风险是:模型会把无依据补全当作监督信号,学习成一种回答模式。
考点:监督微调会逐 token 学习标注答案,数据中的错误示范不会因为进入训练集就自动被模型识别为错误。
3. 类别不平衡下的异常检测
正例占比很低时,模型全部预测为正常也可能取得很高 Accuracy。更合理的补充指标包括查准率、召回率、F1 和 PR-AUC。
考点:类别不平衡时不能只看 Accuracy;PR-AUC 对稀少正例通常更有诊断价值。
4. 卷积层参数量
输入通道数为 3,输出通道数为 16,卷积核大小为 $3\times3$,每个输出通道有一个 bias。参数量为:
\[3\times16\times3\times3+16=448\]5. 方差的线性变换
若随机变量 $X$ 满足给定方差,令 $Y=aX+b$,则:
\[Var(Y)=a^2Var(X)\]常数项不影响方差,系数需要平方。
6. 条件概率
如果事件 $A$ 发生 30 次,其中 $A$ 与 $B$ 同时发生 12 次,则按频率估计:
\[P(B\mid A)=\frac{12}{30}=0.4\]7. 其他高频基础点
- sigmoid 将实数映射到 $(0,1)$,可解释为二分类概率;
- BPE、SentencePiece 等子词分词方法将文本切成 token,并在词表规模与未登录词之间做折中;
- 栈遵循后进先出;
- 选择排序第一趟会把未排序区间的最小值交换到首位。
第 2 题:相邻逆序交换洗牌
题目描述
给定一个长度为 $n$ 的排列 $p$,执行恰好 $k$ 次局部操作。每次从左到右找到第一个满足
\[p_i > p_{i+1}\]的位置,交换 $p_i$ 与 $p_{i+1}$。如果当前排列不存在这样的相邻逆序,则本次操作不改变排列。
输出所有操作结束后的排列。
输入输出
每组数据给出 $n,k$ 和一个长度为 $n$ 的排列。输出最终排列。数据范围允许 $k$ 很大,因此不能简单执行 $k$ 轮完整扫描。
核心思路
找最左侧相邻逆序并交换,本质上是冒泡排序的一步。每交换一次相邻逆序对,排列的逆序对数量恰好减少 1;当排列变成升序后,后续操作全部空转。
因此,真正需要执行的交换次数最多是初始逆序对数量,而不是 $k$。实现时可以维护扫描指针:
- 继续从上次位置寻找下降点,避免每次从头扫描;
- 交换后指针最多向左回退一格,因为交换到左侧的是较小值;
- 交换次数达到 $k$,或扫描到末尾时停止。
Python 参考实现
import sys
def shuffle_times(p, k):
n = len(p)
i = 0
done = 0
while done < k:
while i + 1 < n and p[i] <= p[i + 1]:
i += 1
if i + 1 >= n:
break
p[i], p[i + 1] = p[i + 1], p[i]
done += 1
if i > 0:
i -= 1
return p
def solve():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
t = next(it)
ans = []
for _ in range(t):
n = next(it)
k = next(it)
p = [next(it) for _ in range(n)]
ans.append(" ".join(map(str, shuffle_times(p, k))))
print("\n".join(ans))
if __name__ == "__main__":
solve()
复杂度
若实际发生了 $s$ 次交换,扫描指针的均摊移动量为 $O(n+s)$,空间复杂度为 $O(n)$。由于 $s$ 不超过初始逆序对数量,$k$ 很大时也不会真的执行 $k$ 轮空操作。
第 3 题:蜂蜜回文数组最少搬运
题目描述
给定长度为 $n$ 的正整数数组 $a$。一次操作可以从某个位置取出 1 单位,放入另一个不同位置。求最少操作次数,使数组变成回文数组;如果无法做到,输出 $-1$。
关键观察
对称位置 $(i,n-1-i)$ 最终必须变成同一个值。设这两个值为 $x,y$,把它们调整到同一个目标值 $t$ 所需的搬运总量为:
\[|x-t|+|y-t|\]| 当 $t$ 位于 $x$ 和 $y$ 之间时,最小值为 $ | x-y | $。因此先计算所有对称位置的差值: |
每次搬运同时产生一次减少和一次增加,所以还要根据数组长度和总和的奇偶性修正。
奇偶性判断
- 偶数长度:所有元素都属于某个对称 pair。回文数组总和必须为偶数,因此原数组总和为奇数时无解;否则答案为 $D/2$。
- 奇数长度:中间元素可以承接剩余总量,始终有解;答案为 $\lceil D/2\rceil$。
Python 参考实现
import sys
def min_moves(a):
n = len(a)
d = sum(abs(a[i] - a[n - 1 - i]) for i in range(n // 2))
if n % 2 == 0:
if sum(a) % 2:
return -1
return d // 2
return (d + 1) // 2
def solve():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
t = next(it)
ans = []
for _ in range(t):
n = next(it)
a = [next(it) for _ in range(n)]
ans.append(str(min_moves(a)))
print("\n".join(ans))
if __name__ == "__main__":
solve()
复杂度
每组只需扫描数组并计算对称差值,时间复杂度为 $O(n)$,额外空间复杂度为 $O(1)$(不计输入存储)。
第 4 题:连通图边权和最大化
题目描述
给定权值数组 $a$,构造整数数组 $b$。对任意两个不同节点 $i,j$:
- 若 $b_i\ne b_j$,就在两点之间连一条边,边权为 $\min(a_i,a_j)$;
- 若 $b_i=b_j$,两点之间不连边。
要求构造出的图连通,并最大化所有边权之和。
分层化简
$b$ 的具体数值并不重要,重要的是它把节点分成了若干层。不同层之间全部有边,同层之间没有边。为了保证连通,至少需要两层($n=1$ 时答案为 0)。
将 $a$ 按降序排列。若一个点位于某层,而它下面有若干点,则它会作为较小端点参与与这些点的连边,贡献为这些下方点的权值之和。于是应优先让较大的权值处在更低的层,尽可能被更多边计入。
定义降序数组的前缀和:
\[P_t=\sum_{i=0}^{t-1}a_i\]枚举分层的切分位置,使用前缀和计算每种层数的贡献。由于 $a$ 已降序,$P_t$ 的增量单调不增,前缀和序列先增后降,可以线性扫描峰值和最优切分点。
参考实现
import sys
def best_sum(a):
n = len(a)
if n <= 1:
return 0
a.sort(reverse=True)
pref = [0] * n
for t in range(1, n):
pref[t] = pref[t - 1] + a[t - 1]
peak = 1
for t in range(2, n):
if pref[t] > pref[peak]:
peak = t
# 首层切在峰值之前的情况
sum_pref = 0
min_sum_pref = 0
for k in range(1, peak):
sum_pref += pref[k]
min_sum_pref = min(min_sum_pref, sum_pref)
sum_pref += pref[peak]
best = (sum_pref - min_sum_pref) + (n - 1 - peak) * pref[peak]
# 首层越过峰值后的情况
for j in range(peak + 1, n):
best = max(best, (n - j) * pref[j])
return best
def solve():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
t = next(it)
ans = []
for _ in range(t):
n = next(it)
a = [next(it) for _ in range(n)]
ans.append(str(best_sum(a)))
print("\n".join(ans))
if __name__ == "__main__":
solve()
复杂度
排序是主要开销,时间复杂度为 $O(n\log n)$,前缀和与最优切分扫描为 $O(n)$,额外空间复杂度为 $O(n)$。
复盘建议
这场题目覆盖了算法基础、机器学习常识和构造/贪心问题。值得重点复习:
- 看到操作次数很大时,先找“真正发生变化的次数”的上界;
- 对称位置问题优先尝试配对差值和总量守恒;
- 构造题先忽略具体数值,寻找等价的分组或分层结构;
- ACM 模式下统一使用快速输入,并提前检查 64 位整数溢出;
- 先做能证明复杂度的解法,再考虑微优化。