淘天笔试 淘天笔试题 2026春招实习笔试真题解析
笔试时间:2026年3月25日
往年笔试合集:
第一题:糖果分配极值
题目
圣诞老人有 n 种糖果,第 i 种糖果有 cᵢ 个。他要把这些糖果分给 k 个小朋友。分配规则如下:
- 每一种糖果必须全部分完;
- 对于任意一种糖果,任意两个小朋友所分得的该种糖果的数量之差不超过 1。
在所有合法分配中,任选一个小朋友,统计其最终得到的糖果总数;求该数值在所有合法分配中可能达到的最小值与最大值。
换句话说,求在所有合法分配方案中,任意一个小朋友所能获得的糖果总数的最小值与最大值。
输入描述
每个测试文件均包含多组测试数据,第一行输入一个整数 T (1 ≤ T ≤ 2×10⁵) 代表数据组数,每组测试数据描述如下:
第一行输入两个整数 n, k (1 ≤ n ≤ 2×10⁵, 1 ≤ k ≤ 10⁹);
第二行输入 n 个整数 c₁, …, cₙ (0 ≤ cᵢ ≤ 10⁹)。
除此之外,保证单个测试文件的 n 之和不超过 5×10⁵。
输出描述
对于每组测试数据,输出两个整数:在所有合法分配中,单个小朋友可能得到的糖果总数的最小值与最大值。
样例输入
3 3 2 5 2 7 4 3 0 0 0 0 2 5 10 1
样例输出
6 8 0 0 2 3
参考题解
解题思路:
对某一种糖果,设数量为 cnt。因为要求任意两个小朋友分到的该种糖果数量差不超过 1,所以每个人拿到的数量只可能是 cnt / k 或 cnt / k + 1。因此,对某个固定小朋友来说,这一类糖果贡献的最小值是 cnt / k,最大值是 cnt / k + (cnt % k != 0 ? 1 : 0)。把所有种类逐个累加即可。
C++:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
long long k;
cin >> n >> k;
long long mn = 0, mx = 0;
for (int i = 0; i < n; i++) {
long long cnt;
cin >> cnt;
mn += cnt / k;
mx += cnt / k;
if (cnt % k) mx++;
}
cout << mn << ' ' << mx << '\n';
}
return 0;
}
Java:
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int T = Integer.parseInt(br.readLine().trim());
while (T-- > 0) {
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
long k = Long.parseLong(st.nextToken());
st = new StringTokenizer(br.readLine());
long mn = 0, mx = 0;
for (int i = 0; i < n; i++) {
long cnt = Long.parseLong(st.nextToken());
mn += cnt / k;
mx += cnt / k;
if (cnt % k != 0) mx++;
}
sb.append(mn).append(' ').append(mx).append('\n');
}
System.out.print(sb);
}
}
Python:
import sys
input = sys.stdin.readline
T = int(input())
for _ in range(T):
n, k = map(int, input().split())
c = list(map(int, input().split()))
mn = 0
mx = 0
for cnt in c:
mn += cnt // k
mx += cnt // k
if cnt % k != 0:
mx += 1
print(mn, mx)
第二题:最大字典序公共子序列
题目
给定两个长度为 n 的排列 p 和 q(均为 1∼n 的排列,元素两两不同),请在它们的公共子序列中,找到字典序最大的序列并输出。
输入描述
第一行输入一个整数 n (1 ≤ n ≤ 2×10⁵),表示排列的长度。
第二行输入 n 个整数 p₁, p₂, …, pₙ,表示排列 p。
第三行输入 n 个整数 q₁, q₂, …, qₙ,表示排列 q。
保证 p 与 q 都是 1∼n 的排列。
输出描述
第一行输出一个整数 k,表示答案序列的长度。
第二行输出 k 个整数,为这条字典序最大的公共子序列。
样例输入
5 2 5 3 1 4 5 2 4 3 1
样例输出
2 5 4
样例解释
两序列的公共子序列中,以 5 开头后,能够继续选择的最大元素为 4,得到 [5, 4]。与 [5, 1] 相比,第二个元素更大,因此 [5, 4] 字典序更大。
参考题解
解题思路:
因为 p 和 q 都是排列,每个数只出现一次,所以公共子序列本质上只和"相对顺序"有关。要让字典序最大,就要优先让前面的数尽量大,因此可以从大到小枚举数值 v。如果 v 在两个排列中的位置都还在当前答案末尾之后,就可以把它加入答案。这样贪心选出来的序列就是字典序最大的公共子序列。
C++:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> p(n), q(n);
vector<int> pos_p(n + 1), pos_q(n + 1);
for (int i = 0; i < n; i++) {
cin >> p[i];
pos_p[p[i]] = i;
}
for (int i = 0; i < n; i++) {
cin >> q[i];
pos_q[q[i]] = i;
}
int last_p = -1, last_q = -1;
vector<int> ans;
for (int val = n; val >= 1; val--) {
if (pos_p[val] > last_p && pos_q[val] > last_q) {
ans.push_back(val);
last_p = pos_p[val];
last_q = pos_q[val];
}
}
cout << ans.size() << '\n';
for (int i = 0; i < (int)ans.size(); i++) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
return 0;
}
Java:
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
int[] posP = new int[n + 1];
int[] posQ = new int[n + 1];
StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
int val = Integer.parseInt(st.nextToken());
posP[val] = i;
}
st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
int val = Integer.parseInt(st.nextToken());
posQ[val] = i;
}
int lastP = -1, lastQ = -1;
List<Integer> ans = new ArrayList<>();
for (int val = n; val >= 1; val--) {
if (posP[val] > lastP && posQ[val] > lastQ) {
ans.add(val);
lastP = posP[val];
lastQ = posQ[val];
}
}
StringBuilder sb = new StringBuilder();
sb.append(ans.size()).append('\n');
for (int i = 0; i < ans.size(); i++) {
if (i > 0) sb.append(' ');
sb.append(ans.get(i));
}
sb.append('\n');
System.out.print(sb);
}
}
Python:
import sys
input = sys.stdin.readline
def main():
n = int(input())
p = list(map(int, input().split()))
q = list(map(int, input().split()))
pos_p = [0] * (n + 1)
pos_q = [0] * (n + 1)
for i in range(n):
pos_p[p[i]] = i
for i in range(n):
pos_q[q[i]] = i
last_p = -1
last_q = -1
ans = []
for val in range(n, 0, -1):
if pos_p[val] > last_p and pos_q[val] > last_q:
ans.append(val)
last_p = pos_p[val]
last_q = pos_q[val]
print(len(ans))
print(' '.join(map(str, ans)))
main()
第三题:第k个喜欢数
题目
Tk 不喜欢能被 3 整除的正整数,也不喜欢十进制表示中包含数字 '3' 的正整数。现在他要将所有他喜欢的正整数按升序排成一个序列。给定正整数 k,请你找出这个序列的第 k 个数。
友情提醒:第 10¹⁸ + 1 项为 10 995 467 216 611 448 857。
输入描述
第一行输入一个整数 t (1 ≤ t ≤ 10⁴),表示测试用例的数量;
接下来
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南

查看15道真题和解析