淘天笔试 淘天笔试题 0816
笔试时间:2025年8月16日
往年笔试合集:
第一题:混乱的数字
给定一个长度为 ( n ) 的数组 ({a_1, a_2, \ldots, a_n}),数组中的元素互不相同。我们希望最后能够按严格从大到小的顺序将所有元素输出。为此,我们决定使用若干个队列来对元素进行整理,每一个队列都是先进先出(FIFO)的结构。整个过程分为两个阶段:
分配阶段:按照数组的原始顺序(从 ( i = 1 ) 到 ( n )),选择任意一个队列将 ( a_i ) 放入。然而,如果队列中已经存在元素,则只能将 ( a_i ) 放入与队列中已有元素相同奇偶性的队列中。
输出阶段:当所有元素都分配完毕后,开始输出。每一步,选择任意一个队列,取出其头部的元素并输出。目标是在整个输出过程中得到一个严格递减的序列。
显然,如果不限制队列的数量,那么可以轻松的完成任务。所以,你需要求解,至少需要多少个队列,才能完成上述流程并保证最终输出严格递减。
输入描述
第一行输入一个整数 ( n )((1 \le n \le 2 \times 10^5)),表示数组的长度。
第二行输入 ( n ) 个整数 ( a_1, a_2, \ldots, a_n )((0 \le a_i \le 10^9)),表示数组中的元素。
输出描述
输出一个整数,表示至少需要多少个队列,才能完成上述流程并保证最终输出严格递减。
样例输入
9
8 4 2 5 3 7 1 6 0
样例输出
4
说明:在这个样例中,其中一个分配阶段的展示是:第一个队列依次放入 (8, 4, 2);第二个队列依次放入 (5, 3, 1);第三个队列放入 (7);最后第四个队列依次放入 (6, 0)。可以证明四个队列是所需要的最少的队列的数量。
参考题解
C++:
#include <bits/stdc++.h>
using namespace std;
// 计算数组的 LIS 长度(非严格递增:使用 lower_bound)
int lis_length(const vector<long long>& arr) {
if (arr.empty()) return 0;
vector<long long> tails; // tails[len-1] = 长度为 len 的递增子序列的最小尾值
for (auto x : arr) {
auto it = lower_bound(tails.begin(), tails.end(), x);
if (it == tails.end()) tails.push_back(x);
else *it = x;
}
return (int)tails.size();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<long long> evens, odds;
evens.reserve(n);
odds.reserve(n);
for (int i = 0; i < n; ++i) {
long long x; cin >> x;
if ((x & 1LL) == 0) evens.push_back(x);
else odds.push_back(x);
}
cout << lis_length(evens) + lis_length(odds) << "\n";
return 0;
}
Java:
import java.io.*;
import java.util.*;
public class Main {
// 简单快速输入
static class FastScanner {
private final InputStream in;
private final byte[] buffer = new byte[1 << 16];
private int ptr = 0, len = 0;
FastScanner(InputStream is) { this.in = is; }
private int read() throws IOException {
if (ptr >= len) {
len = in.read(buffer);
ptr = 0;
if (len <= 0) return -1;
}
return buffer[ptr++];
}
long nextLong() throws IOException {
int c; long sgn = 1, x = 0;
do { c = read(); } while (c <= ' ');
if (c == '-') { sgn = -1; c = read(); }
while (c > ' ') { x = x * 10 + (c - '0'); c = read(); }
return x * sgn;
}
int nextInt() throws IOException { return (int) nextLong(); }
}
// 计算数组的 LIS 长度(非严格递增:lower_bound)
static int lisLength(List<Long> arr) {
if (arr.isEmpty()) return 0;
ArrayList<Long> tails = new ArrayList<>();
for (long x : arr) {
int pos = lowerBound(tails, x);
if (pos == tails.size()) tails.add(x);
else tails.set(pos, x);
}
return tails.size();
}
// 在非降序列表中找第一个 >= target 的位置
static int lowerBound(ArrayList<Long> a, long target) {
int l = 0, r = a.size();
while (l < r) {
int mid = (l + r) >>> 1;
if (a.get(mid) >= target) r = mid;
else l = mid + 1;
}
return l;
}
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner(System.in);
int n = fs.nextInt();
ArrayList<Long> evens = new ArrayList<>(n);
ArrayList<Long> odds = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
long x = fs.nextLong();
if ((x & 1L) == 0) evens.add(x);
else odds.add(x);
}
int ans = lisLength(evens) + lisLength(odds);
System.out.println(ans);
}
}
Python:
import sys import bisect input = sys.stdin.read data = input().split() n = int(data[0]) a = list(map(int, data[1:])) evens = [x for x in a if x % 2 == 0] odds = [x for x in a if x % 2 != 0] def lis_length(arr): ifnot arr: return0 tails = [] for num in arr: pos = bisect.bisect_left(tails, num) if pos == len(tails): tails.append(num) else: tails[pos] = num return len(tails) print(lis_length(evens) + lis_length(odds))
第二题:小C的矩阵
给定一个 ( n \times m ) 的矩阵。矩阵第 ( i ) 行第 ( j ) 列的初始值记为 ( v_{i,j} )。
给定一个长度为 ( n ) 的数组 ({a_1, a_2, \ldots, a_n}),以及一个长度为 ( m ) 的数组 ({b_1, b_2, \ldots, b_m})。
对于每个位置 ((i, j)) 的权值为:( w_{i,j} = v_{i,j} \times (a_i + b_j) )
你可以对矩阵进行任意次以下操作(数组 ( a, b ) 不会随操作改变):
- 交换矩阵中任意两行所有同列的值(行变换);
- 交换矩阵中任意两列所有同行的值(列变换)。
请通过这些操作,使得矩阵的权值和最大化。
【名词解释】
- 行变换:行变换就是选取两个不同的行,将它们互换。
- 列变换:列变换就是选取两个不同的列,将它们互换。
输入描述
第一行输入两个整数 ( n, m )((1 \le n, m \le 10^3)),分别表示矩阵的行数和列数。
第二行输入 ( n ) 个整数 ( a_1, a_2, \ldots, a_n )((1 \le a_i \le 10^4)),表示每行的权值。
第三行输入 ( m ) 个整数 ( b_1, b_2, \ldots, b_m )((1 \le b_j \le 10^4)),表示每列的权值。
接下来 ( n ) 行,每行输入 ( m ) 个整数 ( v_{i,1}, v_{i,2}, \ldots, v_{i,m} )((1 \le v_{i,j} \le 10^4)),表示矩阵的初始值。
输出描述
输出一个整数,表示通过任意次行变换或列变换后,矩阵权值和的最大值。
样例输入
1 3
1
1 2 3
3 1 2
样例输出
20
参考题解
C++:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
if (!(cin >> n >> m)) return 0;
vector<long long> a(n), b(m);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int j = 0; j < m; ++j) cin >> b[j];
vector<long long> row_sums(n, 0), col_sums(m, 0);
// 逐行读入矩阵并累加行和、列和,避免存整矩阵
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
long long v; cin >> v;
row_sums[i] += v;
col_sums[j] += v;
}
}
// 降序排序以最大化点积
sort(a.begin(), a.end(), greater<long long>());
sort(b.begin(), b.end(), greater<long long>());
sort(row_sums.begin(), row_sums.end(), greater<long long>());
sort(col_sums.begin(), col_sums.end(), greater<long long>());
long long max_total_weight = 0;
for (int i = 0; i < n;
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南
