【笔试刷题】携程-2026.03.12-算法岗-改编真题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的
春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗
携程-2026.03.12-算法岗
这套算法岗题中,第 1、2、4 题与研发岗一致,第 3 题替换成了无监督学习流程。整体覆盖规律观察、分类讨论、机器学习工程实现,以及位运算 SOS DP 四个方向。下面按题目顺序整理四道题的完整内容。
题目1:吉祥物尾牌
问题描述
A 先生正在给一批活动吉祥物挂牌编号。
第 个吉祥物的尾牌编号规则很特别,它被定义为:
也就是常见的阶乘 。
现在 A 先生只关心这个大数的最后一位数字。给定整数 ,请输出
的个位数字。
输入格式
输入一行,一个整数 。
输出格式
输出一个整数,表示 的个位数字。
样例输入
3
样例输出
6
数据范围
| 样例 | 解释说明 |
|---|---|
| 样例1 | |
若输入为 1 |
题解
这题真正考的不是怎么去算阶乘,而是观察个位数字什么时候会固定下来。
只要 ,那么
的乘积里一定同时包含:
- 一个因子
- 一个因子
而 ,这就意味着整个乘积末尾至少会出现一个
。所以:
- 当
时,答案一定是
。
剩下只需要处理前面几个很小的情况:
,个位是
也就是说,这题根本不需要真的去做大整数乘法,只要分类讨论即可。
时间复杂度是 ,空间复杂度也是
。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def solve():
n = int(input())
# 当 n >= 5 时,阶乘里一定出现因子 2 和 5,
# 所以末尾至少有一个 0。
if n >= 5:
print(0)
return
# 直接保存 1! 到 4! 的个位数字。
last = [0, 1, 2, 6, 4]
print(last[n])
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n;
cin >> n;
// n >= 5 时,n! 一定包含因子 10,个位固定为 0。
if (n >= 5) {
cout << 0 << '\n';
return 0;
}
// 记录小范围阶乘的个位数字。
int last[5] = {0, 1, 2, 6, 4};
cout << last[n] << '\n';
return 0;
}
- Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
long n = Long.parseLong(br.readLine().trim());
// 当 n >= 5 时,阶乘末尾一定出现 0。
if (n >= 5) {
System.out.println(0);
return;
}
int[] last = {0, 1, 2, 6, 4};
System.out.println(last[(int) n]);
}
}
题目2:盲盒素数组装
问题描述
K 小姐正在准备一批盲盒礼包,总价值一共是 。
她打算把这份价值拆成若干个素数价值的小礼包,允许重复使用同一个素数。假设最终一共拆出了 个素数。
不过运营同学额外提出了一个限制:
- 所有被选中的奇素数,也就是除
以外的素数,其数量必须是
的正整数倍;
- 奇素数的数量不能是
。
在满足以上条件的前提下,K 小姐希望素数礼包的总个数 尽可能大。请输出这个最大的
;如果不存在任何合法方案,输出
-1。
输入格式
第一行输入一个整数 ,表示测试数据组数。
接下来 行,每行输入两个整数
。
输出格式
对于每组数据,输出一行一个整数,表示最大的素数个数;若无解,输出 -1。
样例输入
4
6 2
2 1
30 4
1000000000000 1
样例输出
2
-1
13
499999999999
数据范围
| 样例 | 解释说明 |
|---|---|
| 样例1 的第一组 | |
| 样例1 的第二组 |
题解
如果想让素数个数尽量多,那么就应该尽量使用最小的素数。
最小的素数是 ,次小的奇素数是
。由于题目对奇素数数量有限制,所以最优方案一定长这样:
- 用尽可能少的奇素数去满足条件;
- 这些奇素数全都取成最小的
;
- 剩下的部分全部用
去填。
这样才会让素数总个数最大。
接下来问题就变成:奇素数最少要取多少个。
设奇素数个数是 odd。
因为:
odd必须是的正整数倍;
- 每个奇素数都是奇数,若用了
odd个奇数,那么它们的总和奇偶性与odd相同; - 剩余部分全部由若干个
组成,因此剩余和一定是偶数;
所以 odd 必须与 同奇偶,否则剩下的数不可能完全由
填满。
于是:
- 如果
和
同奇偶,那么最少取
odd = m; - 如果
是奇数、
是偶数,那么最少取
odd = 2m; - 如果
是偶数、
是奇数,那么无论取多少个
的倍数,奇素数个数都还是偶数,不可能与奇数的
同奇偶,因此无解。
确定了 odd 之后,还要保证最小总和不超过 。
因为每个奇素数最小只能是 ,所以至少需要:
若这个值已经大于 ,同样无解。
否则,剩下的部分全部补成 即可。设补了
two 个 ,那么:
总素数个数就是:
整题每组数据只需要做常数次判断,时间复杂度是 ,空间复杂度也是
。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def solve():
t = int(input())
out = []
for _ in range(t):
n, m = map(int, input().split())
# odd 表示最少需要选多少个奇素数。
if (n & 1) == (m & 1):
odd = m
elif m & 1:
odd = 2 * m
else:
out.append("-1")
continue
# 每个奇素数最小只能取 3,先判断最小和是否超出。
if 3 * odd > n:
out.append("-1")
continue
# 剩余部分全部由 2 填充,总个数化简为 (n - odd) // 2。
out.append(str((n - odd) // 2))
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128_t;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
i64 n, m;
cin >> n >> m;
i128 odd;
// 先求满足奇偶性的最少奇素数个数。
if ((n & 1LL) == (m & 1LL)) {
odd = m;
} else if (m & 1LL) {
odd = (i128)2 * m;
} else {
cout << -1 << '\n';
continue;
}
// 最小奇素数都是 3,如果连最小和都放不下就无解。
if ((i128)3 * odd > (i128)n) {
cout << -1 << '\n';
continue;
}
// 剩余都放成 2,答案可直接化简。
i128 ans = ((i128)n - odd) / 2;
cout << (long long)ans << '\n';
}
return 0;
}
- Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(br.readLine().trim());
StringBuilder sb = new StringBuilder();
for (int cs = 0; cs < t; cs++) {
StringTokenizer st = new StringTokenizer(br.readLine());
long n = Long.parseLong(st.nextToken());
long m = Long.parseLong(st.nextToken());
long odd;
// odd 表示最少需要的奇素数个数。
if ((n & 1L) == (m & 1L)) {
odd = m;
} else if ((m & 1L) == 1L) {
odd = 2L * m;
} else {
sb.append(-1).append('\n');
continue;
}
// 先判断最小可能的奇素数总和是否超出 n。
if (odd > n / 3L) {
sb.append(-1).append('\n');
continue;
}
long ans = (n - odd) / 2L;
sb.append(ans).append('\n');
}
System.out.print(sb);
}
}
题目3:无监督学习流程
问题描述
在仅使用 numpy/pandas/scikit-learn 的前提下,完成如下无监督三步流程,并对测试集输出聚类标签。
- 预处理:对训练集与测试集的所有列,先用训练集该列均值填充
NaN;如果某一行列数不足最大列数,则缺失位置也视为NaN。随后用StandardScaler在训练集上fit,并对训练集、测试集做同一变换。 - 降维(PCA):选择最小的
pca_dim,使得前pca_dim个主成分的累计解释方差不小于0.95。若不需要降维,则保留原始标准化后的全部维度。 - 选择
k:从候选集合k_list中枚举每个k,在训练集上训练KMeans(n_clusters=k, random_state=42, n_init=10),并计算训练集的silhouette_score。若k=1、k非法,或轮廓系数无法计算,则该k的得分记为-1。取得分最高的k_star;若并列,取较小的k。 - 输出测试标签:使用
k_star在训练集上重新拟合KMeans,并对测试集执行predict。直接输出KMeans的原生标签,不再重排。
输入格式
输入为一行字典,包含以下三个键:
train:训练特征,形如二维数组;test:测试特征,形如二维数组;k_list:候选聚类数列表。
允许数组元素中出现 NaN。若不同样本的列数不一致,则按最大列数对较短样本右侧补 NaN 后再统一处理。
输出格式
输出一个字典,包含三个键:
pca_dim:最终选中的 PCA 维度;k_star:最优聚类数;test_pred:与测试集行顺序对应的整型标签列表。
样例输入
{"train":[[0.0,0.0],[0.2,0.1],[5.0,5.1],[4.9,4.8]],"test":[[0.1,NaN],[5.2,4.9]],"k_list":[2,3]}
样例输出
{"pca_dim":1,"k_star":2,"test_pred":[0,1]}
数据范围
2 <= len(train) <= 2001 <= len(test) <= 2001 <= 最大列数 <= 202 <= k <= 8
| 项目 | 说明 |
|---|---|
候选 k |
不保证都合法,需要按题意把非法情况记为 -1 |
| 标签输出 | 直接输出 KMeans.predict 的原生标签,不做重排 |
题解
这题的难点不在某个单点算法,而在于三个步骤必须严格按题意衔接,否则答案很容易和标答不一致。
第一步是统一数据形状与缺失值处理。由于输入可能出现“某些样本列数更短”的情况,所以应先把所有样本补到相同列数,缺失位置都视为 。接着只用训练集统计每一
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力