【笔试刷题】OPPO-2026.03.22-改编真题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的
春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗
OPPO-2026.03.22
这套题的层次也比较顺:第 1 题核心是把式子改写成“总和减去三倍极值”,看清最优分组后可以一眼写完;第 2 题是经典的降序贪心,把每个数一路除以 往下压,检查能否各占一个目标值;第 3 题表面在看乘积,真正维护的却只是区间里质因子
和
的个数,双指针就够了。
题目一:三组和差最大值
关键不在枚举分组,而是先意识到标签可以重排。式子只和“让 尽量小”或“让
尽量大”两种极端情况有关,所以答案能压成两个公式取最大值。
难度:简单
题目二:半衰变成排列
每个数能走到哪些值,只取决于不断向下除以 的链。把数组按从大到小处理,总是尽量占住当前还能取到的最大空位,这样不会堵死后面的更小数字。
难度:中等
题目三:末尾零达标区间
子数组乘积末尾零的个数,本质上等于区间内质因子 和
次数的较小值。把每个元素拆成这两个贡献后,问题就变成标准的单调双指针统计。
难度:中等
1. 三组和差最大值
问题描述
小基 手里有一批整数权重,她想把它们分成三组做对比实验。每个元素必须且只能被放进其中一组,并且三组都不能为空。
设三组元素和分别为 ,请你求出:
的最大可能值。
输入格式
第一行输入一个整数 ,表示数组长度。
第二行输入 个整数
,表示数组元素。
输出格式
输出一个整数,表示所求表达式的最大值。
样例输入
4
1 2 3 4
样例输出
11
| 样例 | 解释说明 |
|---|---|
| 样例1 | 可以把三组分成 |
数据范围
题解
先记数组总和为:
假设某次分组后的三个组和是 ,并且满足:
先看标签怎么放最优
题目里的三组是有编号的,所以同一组和集合,换一下谁叫 ,答案也会变。
如果把最小值记成 ,最大值记成
,那么:
- 让
时,答案是
- 让
时,答案是
把中间值放到 只会得到
,一定不如把极值放到中间更优。
所以问题只剩两件事:
- 最小的组和能做到多小;
- 最大的组和能做到多大。
情况一:让
尽量小
因为所有 都是正数,所以非空组和的最小值一定是数组里的最小元素。
设最小元素为 ,把它单独拿成一组,就能得到:
这时答案就是:
情况二:让
尽量大
想让某一组和尽量大,就要让另外两组尽量小。
由于这两组都必须非空,而且元素都是正数,所以最优做法一定是把最小的两个元素各自单独拿出来。设第二小元素为 ,那么最大的组和就是:
此时答案为:
最终答案
因此直接取两种极端情况的较大值即可:
只要一趟扫描求出总和、最小值和次小值,答案就出来了。
复杂度分析
- 时间复杂度:
- 空间复杂度:
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def solve() -> None:
n = int(input())
arr = list(map(int, input().split()))
s = 0
a = 10**30
b = 10**30
for x in arr:
s += x
# 维护最小值和次小值。
if x < a:
b = a
a = x
elif x < b:
b = x
ans1 = s - 3 * a
ans2 = 2 * s - 3 * (a + b)
print(max(ans1, ans2))
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
long long s = 0;
long long a = (long long)4e18, b = (long long)4e18;
for (int i = 0; i < n; ++i) {
long long x;
cin >> x;
s += x;
// 维护全局最小值和次小值。
if (x < a) {
b = a;
a = x;
} else if (x < b) {
b = x;
}
}
long long v1 = s - 3 * a;
long long v2 = 2 * s - 3 * (a + b);
cout << max(v1, v2) << '\n';
return 0;
}
- Java
import java.io.*;
import java.util.*;
public class Main {
static class FastScanner {
private final BufferedInputStream in = new BufferedInputStream(System.in);
private final byte[] buf = new byte[1 << 16];
private int ptr = 0, len = 0;
private int read() throws IOException {
if (ptr >= len) {
len = in.read(buf);
ptr = 0;
if (len <= 0) {
return -1;
}
}
return buf[ptr++];
}
long nextLong() throws IOException {
int c;
while ((c = read()) <= ' ') {
if (c == -1) {
return -1;
}
}
long sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
long val = 0;
while (c > ' ') {
val = val * 10 + c - '0';
c = read();
}
return val * sgn;
}
}
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner();
int n = (int) fs.nextLong();
long sum = 0;
long mn1 = Long.MAX_VALUE;
long mn2 = Long.MAX_VALUE;
for (int i = 0; i < n; i++) {
long x = fs.nextLong();
sum += x;
// 同步维护最小值和次小值。
if (x < mn1) {
mn2 = mn1;
mn1 = x;
} else if (x < mn2) {
mn2 = x;
}
}
long ans1 = sum - 3L * mn1;
long ans2 = 2L * sum - 3L * (mn1 + mn2);
System.out.println(Math.max(ans1, ans2));
}
}
2. 半衰变成排列
问题描述
小兰拿到一个长度为 的整数数组
。她可以对数组做任意次操作,每次选择一个位置
,并把:
也就是说,把这个数替换成它除以 后向下取整的结果。
她想知道,能否经过若干次操作后,让整个数组恰好变成一个长度为 的排列,也就是数组中的数正好是
,并且每个值出现一次。
输入格式
第一行输入一个整数 ,表示测试数据组数。
对于每组数据:
- 第一行输入一个整数
;
- 第二行输入
个整数
。
保证所有测试数据中 的总和不超过
。
输出格式
对于每组测试数据,如果存在可行操作方案,输出 YES;否则输出 NO。
答案大小写不限。
样例输入
5
3
1 2 4
3
1 2 6
1
1
2
1 536870911
5
25752 3010 1188 126 270
样例输出
NO
YES
YES
NO
YES
| 样例 | 解释说明 |
|---|---|
| 样例2 | 把第三个数 |
数据范围
- 单个测试文件内
题解
每个数能变成什么,其实非常固定。
从某个 出发,经过若干次操作后,它只可能落在下面这条链上:
所以整道题可以改写成:
能不能给每个原数组元素分配一个不同的目标值,使这些目标值恰好是
,并且每个目标值都在对应元素的“不断除以
”链上。
为什么按从大到小贪心
大的数更灵活,它能一路往下缩成很多更小的值;小的数选择更少。
如果我们先处理小数,可能会把某个关键位置占掉,导致大数被迫降得过头。反过来,先处理大的数,并且让它尽量占住当前还能取得的最大空位,后面的小数只会更容易安排。
所以做法是:
- 把数组按从大到小排序;
- 依次处理每个数
;
- 只要
或者
已经被别人占用,就不断令
;
- 如果最后变成了
,说明这个数再也放不进排列里,答案就是
NO; - 否则把这个位置占掉,继续处理下一个数。
如果所有数都能成功占到一个不同的目标值,答案就是 YES。
复杂度分析
每个数最多除以 大约
次。
- 时间复杂度:
,其中
是数组元素上界
- 空间复杂度:
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
def solve() -> None:
t = int(input())
out = []
for _ in range(t):
n = int(input())
arr = list(map(int, input().split()))
arr.sort(reverse=True)
vis = [False] * (n + 1)
ok = True
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力