【春招笔试】2025.05.10OPPO春招笔试真题改编
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题(建议用时:90分钟/套)
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上80+套真题改编解析,后续会持续更新的
春秋招合集 -> 互联网必备刷题宝典🔗
题目一:山间跳石过河
1️⃣:找出所有可踩的位置(岸边和石头)
2️⃣:计算相邻可踩位置的最大距离
难度:中等
这道题目的关键在于理解最长的一步最小值就是相邻可踩点之间的最大距离。通过贪心策略,我们只需要一次遍历就能找到最优解,时间复杂度为O(n)。
题目二:数据分块优化传输
1️⃣:判断单个元素是否超过上限,若有则直接返回-1
2️⃣:使用贪心算法尽可能合并相邻元素
3️⃣:当按位或结果超过上限时,开始新的数据块
难度:中等
这道题目结合了位运算和贪心思想,关键在于理解按位或运算的单调性特性。当我们无法将元素加入当前段时,必须开始一个新段,这样可以保证总段数最少。
题目三:彩色方格同游概率
1️⃣:使用BFS/DFS找出所有"异色连通块"
2️⃣:计算各连通块大小的平方和
3️⃣:应用概率公式并约分结果
难度:中等偏难
这道题目需要理解特殊的"异色相邻"规则,并结合图论和概率计算。通过广度优先搜索找出所有连通块后,应用概率公式计算结果。最后需要将分数约分到最简形式,综合考察了多方面的知识点。
01. 山间跳石过河
问题描述
小兰喜欢在山间徒步旅行。一天,她来到了一条湍急的小溪前,需要从左岸跳到右岸。小溪上有一些露出水面的石头,可以作为她跳跃的踏脚点。
小兰只能踩在岸边或者石头上,不能踩在水里。为了确保安全,她想知道在能够成功过河的情况下,最长的一步最短是多少?
输入格式
第一行输入一个整数 (
),代表小溪的宽度。
第二行输入 个整数
(
),如果
,则表示该位置有一块露出水面的石头,可以踩;如果
,则表示该位置是水流,不能踩。
输出格式
输出一个整数,表示小兰过河时最长的一步最小可能是多少。
样例输入
5
0 1 1 0 1
样例输出
2
数据范围
样例1 | 小兰从左岸出发,第一步跳到位置2(距离为2),然后跳到位置3(距离为1),再跳到位置5(距离为2),最后跳到右岸(距离为1)。最长的一步距离为2,且无法通过其他方式使最长的一步更短。 |
题解
这道题要求我们找出过河时最长的一步的最小可能值。
首先,让我们理清题目:我们在河的左岸(位置0),需要到达右岸(位置n+1)。河中有些石头可以踩,有些地方是水流不能踩。我们需要找一条路径,使得其中最长的一步距离尽可能小。
思路分析
关键洞察:最长的一步的最小值就是相邻两个可踩点之间的最大距离。
为什么呢?假设我们有一系列可踩的点(包括左右岸):p₀, p₁, p₂, ..., pₘ(其中p₀是左岸,pₘ是右岸)。不管我们怎么选择路径,我们都必须至少跨过相邻点之间的每一个间隙。所以,如果相邻点之间的最大距离是d,那么我们最长的一步至少是d。
算法步骤
- 初始化左岸位置为0,并记为上一个可踩的位置
- 遍历所有位置,当遇到可踩的石头时:
- 计算当前石头与上一个可踩位置的距离
- 更新最大距离
- 将当前石头位置记为上一个可踩的位置
- 最后,计算右岸与最后一个可踩位置的距离,并再次更新最大距离
- 返回这个最大距离作为答案
这个算法的时间复杂度是O(n),空间复杂度是O(1),非常高效。
为什么这个解法是正确的?
考虑任何过河的方案,我们都必须从一个可踩点到达另一个可踩点。如果相邻可踩点的最大距离是d,那么我们的最长一步不可能小于d。同时,我们可以证明,通过依次跨越每对相邻可踩点,我们可以保证最长的一步就是d。
因此,相邻可踩点之间的最大距离就是我们要求的答案。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
# 读取小溪宽度
n = int(input())
# 读取石头分布
a = list(map(int, input().split()))
# 添加左右岸
stones = [0] + a + [0]
n += 2
# 找出所有可踩位置
pos = []
for i in range(n):
if i == 0 or i == n-1 or stones[i] == 1:
pos.append(i)
# 计算相邻可踩位置的最大距离
max_gap = 0
for i in range(1, len(pos)):
max_gap = max(max_gap, pos[i] - pos[i-1])
# 输出结果
print(max_gap)
- Cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
// 加速输入输出
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 读取小溪宽度
int n;
cin >> n;
// 记录上一个可踩位置(初始为左岸0)
int last = 0;
// 记录最大跨步
int ans = 0;
// 遍历所有位置
for (int i = 1; i <= n; i++) {
int stone;
cin >> stone;
if (stone == 1) {
// 更新最大跨步
ans = max(ans, i - last);
// 更新上一个可踩位置
last = i;
}
}
// 考虑从最后一个石头到右岸的距离
ans = max(ans, n + 1 - last);
// 输出结果
cout << ans;
return 0;
}
- Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取小溪宽度
int n = sc.nextInt();
// 记录上一个可踩位置(初始为左岸0)
int last = 0;
// 记录最大跨步
int ans = 0;
// 遍历所有位置
for (int i = 1; i <= n; i++) {
int stone = sc.nextInt();
if (stone == 1) {
// 更新最大跨步
ans = Math.max(ans, i - last);
// 更新上一个可踩位置
last = i;
}
}
// 考虑从最后一个石头到右岸的距离
ans = Math.max(ans, n + 1 - last);
// 输出结果
System.out.println(ans);
sc.close();
}
}
02. 数据分块优化传输
问题描述
小毛是一名网络工程师,负责优化数据传输系统。他发现一组数据在传输前需要分割成若干个数据块,以便满足网络带宽的限制。
每个数据块由若干个连续的数据包组成。每个数据包有一个特征值,而数据块的"总特征值"是块内所有数据包特征值的按位或(|)运算结果。例如,如果一个数据块包含特征值为[1, 2, 4, 8]的数据包,则其总特征值为1 | 2 | 4 | 8 = 15。
由于网络设备的限制,每个数据块的总特征值不能超过指定的阈值 。小毛希望将数据分割成尽可能少的数据块,同时满足每个数据块的总特征值不超过
。请你帮助他完成这个任务。
输入格式
第一行两个正整数 (
,
),分别表示数据包的数量和总特征值的上限。
第二行 个正整数
(
),表示每个数据包的特征值。
输出格式
输出一行一个整数,如果可以满足要求,则输出最少需要分割的数据块数量;如果无法满足要求,则输出 -1。
样例输入
4 4
1 2 4 3
样例输出
3
数据范围
样例1 | 可以将数据分为以下三个块:[1, 2]、[4]和[3]。第一块的总特征值为1 |
题解
这道题要求我们将一个数组分割成尽可能少的段,使得每段元素按位或的结果不超过给定的上限k。
思路分析
首先,有一个重要的观察:如果数组中存在某个元素ai大于k,那么无论如何都无法将其划入任何一个满足条件的段中(因为单个元素的按位或结果就是元素本身)。在这种情况下,我们应该直接返回-1。
接下来,我们可以使用贪心策略:尽可能让每个段包含更多的元素,这样总段数就会最少。具体来说:
- 维护当前段的按位或结果cur
- 对于每个元素ai,尝试将其加入当前段
- 如果加入后cur | ai ≤ k,则将其加入
- 否则,开始一个新段,并将cur重置为ai
这个贪心策略是正确的,因为按位或操作满足单调性:一旦将某些元素合并到一个段中,其按位或的结果只会增加,不会减少。因此,如果当前元素无法加入当前段,那么它也无法加入当前段的任何子段。
算法步骤
- 初始化:段数cnt=1,当前段的按位或结果cur=0
- 遍历数组中的每个元素ai:
- 如果ai > k,则直接返回-1
- 如果cur | ai ≤ k,则更新cur = cur | ai
- 否则,开始一个新段:cnt++,cur = ai
- 返回段数cnt
时间复杂度分析
算法的时间复杂度是O(n),其中n是数组的长度。我们只需遍历数组一次,并对每个元素进行常数时间的操作。
对于给定的数据范围(n ≤ 2×10^5),这个算法可以高效地解决问题。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
# 读取输入
n, k = map(int, input().split())
a = list(map(int, input().split()))
# 贪心分段
cur = 0 # 当前段的按位或结果
cnt = 1 # 段数
for val in a:
# 如果单个元素超过k,无解
if val > k:
print(-1)
exit()
# 尝试将当前元素加入当前段
if (cur | val) <= k:
cur |= val
else:
# 开始新段
cnt += 1
cur = val
print(cnt)
- Cpp
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
ll k;
cin >> n >> k;
vector<ll> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
ll curr = 0; // 当前段的按位或结果
int cnt = 1; // 段数
for (int i = 0; i < n; i++) {
// 如果单个元素超过k,无解
if (a[i] > k) {
cout << -1;
return 0;
}
// 尝试将当前元素加入当前段
if ((curr | a[i]) <= k) {
curr |= a[i];
} else {
// 开始新段
cnt++;
curr = a[i];
}
}
cout <<
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力