京东笔试 京东笔试题 0816
笔试时间:2025年8月16日
往年笔试合集:
第一题
你有一个长度为n的序列A: a₁,a₂,…,aₙ,你需要进行如下操作至多一次: 选择一个区间[L,R] (1≤L≤R≤n),将a_L,a_{L+1},…,a_R全部加1。即a_L,a_{L+1},…,a_R均变为a_L+1,a_{L+1}+1,…,a_R+1 设变化后的序列为B: b₁,b₂,…,bₙ,你需要最大化inv(A)-inv(B),其中inv()函数表示该序列的逆序对数量,即满足i<j,a_i>a_j的二元组(i,j)个数。换句话说,你需要尽可能地减小序列A的逆序对数量。
输入描述
第一行一个正整数T,表示数据组数; 对于每一组数据,第一行一个正整数n;
第二行n个正整数a₁,a₂,…,aₙ,表示序列A。 1≤n≤10⁵; 1≤T≤5; 1≤a_i≤n。
输出描述
对于每一组数据,输出一个整数,表示最大的inv(A)-inv(B)。
样例输入
3
5
4 2 3 1 5
5
1 2 3 4 5
3
2 1 1
样例输出
2
0
2
说明: 第一组数据,选择区间[3,4],变化后的序列为[4 2 4 2 5],逆序对数量为3;变化前逆序对数量为5。 第二组数据,选择任意区间都不会减小逆序对数量。 第三组数据,选择区间[2,3],变化后的序列为[2 2 2],逆序对数量为0;变化前逆序对数量为2。
参考题解
我们只在一个连续区间把所有数都加 1。对位置 i 的值 v 来说,这个操作只影响两种配对: (1) 右边等于 v−1 的元素会让原本的逆序对消失(赚到的收益 = 右侧 v−1 的个数); (2) 左边等于 v+1 的元素会新产生逆序对(损失 = 左侧 v+1 的个数)。 所以位置 i 的净收益 = 右侧(v−1)数量 − 左侧(v+1)数量。预处理每个取值的全局出现次数 totalCount[·],边扫边维护左侧计数 seenPrefix[·],就能在 O(1) 得到当前位置的增量,把这些增量累加得到把区间一直向右扩的总收益,遍历过程中取到的最大值就是答案。时间复杂度 O(n),空间 O(n)。
C++:
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; if (!(cin >> T)) return 0; while (T--) { int n; cin >> n; vector<int> arr(n); for (int i = 0; i < n; ++i) cin >> arr[i]; vector<int> totalCount(n + 2, 0); for (int v : arr) ++totalCount[v]; vector<int> seenPrefix(n + 3, 0); long long currentGain = 0; long long bestGain = 0; for (int i = 0; i < n; ++i) { bestGain = max(bestGain, currentGain); int v = arr[i]; currentGain += (long long)totalCount[v - 1] - seenPrefix[v - 1] - seenPrefix[v + 1]; ++seenPrefix[v]; } bestGain = max(bestGain, currentGain); cout << bestGain << "\n"; } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { // 快速读入 private static final 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++]; } int nextInt() throws IOException { int c, sgn = 1, x = 0; do { c = read(); } while (c <= 32 && c != -1); if (c == '-') { sgn = -1; c = read(); } while (c > 32 && c != -1) { x = x * 10 + (c - '0'); c = read(); } return x * sgn; } } public static void main(String[] args) throws Exception { FastScanner fs = new FastScanner(System.in); StringBuilder out = new StringBuilder(); int T; try { T = fs.nextInt(); } catch (Exception e) { return; } for (int _t = 0; _t < T; _t++) { int n = fs.nextInt(); int[] arr = new int[n]; for (int i = 0; i < n; i++) arr[i] = fs.nextInt(); int[] totalCount = new int[n + 2]; for (int v : arr) ++totalCount[v]; int[] seenPrefix = new int[n + 3]; long currentGain = 0L; long bestGain = 0L; for (int i = 0; i < n; i++) { if (currentGain > bestGain) bestGain = currentGain; int v = arr[i]; currentGain += (long) totalCount[v - 1] - seenPrefix[v - 1] - seenPrefix[v + 1]; ++seenPrefix[v]; } if (currentGain > bestGain) bestGain = currentGain; out.append(bestGain).append('\n'); } System.out.print(out.toString()); } }
Python:
import sys def main(): data = list(map(int, sys.stdin.buffer.read().split())) it = iter(data) try: T = next(it) except StopIteration: return out_lines = [] for _ in range(T): n = next(it) arr = [next(it) for _ in range(n)] total = [0] * (n + 2) for v in arr: total[v] += 1 seen = [0] * (n + 3) current = 0 best = 0 for v in arr: if current > best: best = current current += total[v - 1] - seen[v - 1] - seen[v + 1] seen[v] += 1 if current > best: best = current out_lines.append(str(best)) sys.stdout.write("\n".join(out_lines)) if __name__ == "__main__": main()
第二题
在一场跳水比赛中,共有n位裁判依次为选手打分(打分为非负整数)。根据比赛规则,需要从所有裁判的打分中,选取连续的m个打分来计算选手的
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南