2025.03.23-pdd春招笔试改编
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题(建议用时:90分钟/套)
- 对照解析查漏补缺
- 配套练习题库
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
春秋招合集 -> 互联网必备刷题宝典🔗
题目一:宝藏方位导航系统
1️⃣:分析移动操作字符串中各方向移动的次数
2️⃣:计算各方向移动后的最终坐标
3️⃣:判断是否恰好到达目标位置
难度:简单
这道题目的核心是理解坐标系中的移动规则,并统计各方向上的移动次数。通过简单的坐标计算,可以在O(n)的时间复杂度内确定最终位置,适用于处理长度不超过10^4的移动序列。
题目二:树形网络优化策略
1️⃣:深度优先搜索计算每个节点的深度和子树大小
2️⃣:计算初始权值和
3️⃣:枚举所有非根节点,找出最大改善量
难度:中等
这道题目需要理解树形结构和节点权值的概念,通过DFS遍历计算节点深度和子树大小,再利用贪心策略找出最优的调整位置。时间复杂度为O(n),能高效处理节点数不超过2×10^5的树形网络。
题目三:视野观察队列分析
1️⃣:使用单调栈从右向左遍历队列
2️⃣:计算每个位置能看到的人数
3️⃣:累加所有位置的视野总和
难度:中等
这道题目巧妙地运用了单调栈的特性来解决视线阻挡问题。通过维护一个递减的栈,可以在O(n)的时间复杂度内计算每个人能看到的其他人数,适用于处理人数不超过10^5的大型队列。
题目四:字符串优化重组策略
1️⃣:统计字符串中每个位置被替换的次数
2️⃣:收集所有被替换的位置并排序
3️⃣:对替换字符排序并优化分配,得到字典序最小的结果
难度:中等偏难
这道题目结合了贪心策略和字符串操作,关键在于理解字典序最小化的原理。通过排序和优化分配,可以在O(n log n)的时间复杂度内找到字典序最小的替换结果,高效处理大规模字符串优化问题。
01. 宝藏方位导航系统
问题描述
小柯是一位冒险家,正在利用一个特殊的导航设备寻找隐藏在二维平面上的宝藏。这个导航设备接收由方位指令构成的序列,每个指令会使小柯向特定方向移动一步。
导航设备使用四个方位指令:
:向北移动一步,即从坐标
到
:向西移动一步,即从坐标
到
:向南移动一步,即从坐标
到
:向东移动一步,即从坐标
到
已知宝藏位于坐标原点 ,小柯的起始位置为坐标
。导航设备给出了一系列方位指令,小柯想知道:按照这些指令行动后,她是否能够恰好到达宝藏的位置?
输入格式
第一行包含一个整数 ,表示测试用例的数量。
对于每个测试用例:
- 第一行包含两个整数
和
,表示小柯的起始坐标。
- 第二行包含一个由字符
、
、
、
组成的字符串,表示导航设备给出的方位指令序列。
输出格式
对于每个测试用例,输出一行:
- 如果小柯能够恰好到达宝藏位置,输出 "YES"
- 否则,输出 "NO"
样例输入
2
2 0
WAS
1 0
WAADS
样例输出
NO
YES
数据范围
- 方位指令序列的长度大于
且不超过
2 2 0 WAS 1 0 WAADS |
第一个测试用例:小柯从坐标 第二个测试用例:小柯从坐标 |
题解
这道题目本质上是在二维平面上跟踪一个点的移动,并判断它是否能到达特定位置。
解题思路非常直观:
- 记录初始位置坐标
- 统计指令序列中每种方位指令的出现次数
- 根据各个方向的移动次数,计算最终位置
- 判断最终位置是否为
假设我们统计了以下数据:
- 向北(
)移动了
次
- 向南(
)移动了
次
- 向东(
)移动了
次
- 向西(
)移动了
次
那么最终的坐标变化为:
轴方向的净变化 =
轴方向的净变化 =
最终坐标 = 初始坐标 + 坐标变化
如果 且
,则说明恰好到达了宝藏位置,输出 "YES";否则输出 "NO"。
时间复杂度分析:
- 统计指令次数需要遍历整个指令序列,时间复杂度为
,其中
是指令序列的长度
- 计算最终位置和判断条件的时间复杂度为
- 总体时间复杂度为
空间复杂度分析:
- 只需要常数级的额外空间来存储计数和坐标,所以空间复杂度为
这种解法高效且直接,对于题目给定的数据范围(指令序列长度不超过10000)完全足够。
参考代码
- Python
import sys
input = lambda:sys.stdin.readline().strip()
def solve():
# 读取测试用例数量
t = int(input())
for _ in range(t):
# 读取初始坐标
x, y = map(int, input().split())
# 读取导航指令序列
inst = input()
# 统计各方向指令次数
cnt_w = inst.count('W') # 北移次数
cnt_a = inst.count('A') # 西移次数
cnt_s = inst.count('S') # 南移次数
cnt_d = inst.count('D') # 东移次数
# 计算最终坐标
fx = x + (cnt_d - cnt_a)
fy = y + (cnt_w - cnt_s)
# 判断是否到达宝藏位置
if fx == 0 and fy == 0:
print("YES")
else:
print("NO")
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
// 优化输入输出速度
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t; // 读取测试用例数量
while (t--) {
int x, y;
cin >> x >> y; // 读取初始坐标
string inst;
cin >> inst; // 读取导航指令序列
// 统计各方向指令次数
int cnt_w = 0, cnt_a = 0, cnt_s = 0, cnt_d = 0;
// 遍历指令序列
for (char c : inst) {
if (c == 'W') cnt_w++; // 北移
else if (c == 'A') cnt_a++; // 西移
else if (c == 'S') cnt_s++; // 南移
else if (c == 'D') cnt_d++; // 东移
}
// 计算最终坐标
int fx = x + (cnt_d - cnt_a);
int fy = y + (cnt_w - cnt_s);
// 判断是否到达宝藏位置
if (fx == 0 && fy == 0) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}
return 0;
}
- Java
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(br.readLine()); // 读取测试用例数量
for (int i = 0; i < t; i++) {
String[] pos = br.readLine().split(" ");
int x = Integer.parseInt(pos[0]); // 初始x坐标
int y = Integer.parseInt(pos[1]); // 初始y坐标
String inst = br.readLine(); // 读取导航指令序列
// 统计各方向指令次数
int cntW = 0, cntA = 0, cntS = 0, cntD = 0;
// 遍历指令序列
for (int j = 0; j < inst.length(); j++) {
char dir = inst.charAt(j);
if (dir == 'W') cntW++; // 北移
else if (dir == 'A') cntA++; // 西移
else if (dir == 'S') cntS++; // 南移
else if (dir == 'D') cntD++; // 东移
}
// 计算最终坐标
int fx = x + (cntD - cntA);
int fy = y + (cntW - cntS);
// 判断是否到达宝藏位置
if (fx == 0 && fy == 0) {
System.out.println("YES");
} else {
System.out.println("NO");
}
}
}
}
02. 树形网络优化策略
问题描述
科技公司正在优化其数据中心的网络拓扑结构,该结构可以表示为一棵由 个节点组成的树。每对相连节点之间的网络连接权重定义为两个节点深度之和。整个树的总权重定义为所有连接的权重之和。
为了优化网络,公司可以执行一次重连操作:选择一个非根节点,将其连同它的子树从原位置断开,然后重新连接到树中的另一个节点上(不能形成环)。
公司希望通过这一次重连操作,最小化整个树的总权重。你需要计算执行最佳重连操作后,树的最小可能总权重。
输入格式
第一行包含一个整数 ,表示节点的数量。
接下来的 行,每行包含两个整数
和
,表示节点
和节点
之间存在一条边。节点编号从
到
。
输出格式
输出一个整数,表示执行一次最佳重连操作后,树的最小可能总权重。
样例输入
5
1 2
1 3
3 4
3 5
样例输出
9
数据范围
- 输入保证构成一棵合法的树
5 1 2 1 3 3 4 3 5 |
初始树结构如下:<br> 1<br> / \<br> 2 3<br> / \<br> 4 5<br> 初始时,每条边的权重为: - 边(1,2):深度(1) + 深度(2) = 1 + 2 = 3 - 边(1,3):深度(1) + 深度(2) = 1 + 2 = 3 - 边(3,4):深度(2) + 深度(3) = 2 + 3 = 5 - 边(3,5):深度(2) + 深度(3) = 2 + 3 = 5 总权重 = 3 + 3 + 5 + 5 = 16 执行最佳重连操作后,可以将节点4及其子树从节点3断开,重新连接到节点2上: <br> 1<br> / \<br> 2 3<br> / \<br>4 5<br> 重连后,每条边的权重为: - 边(1,2):深度(1) + 深度(2) = 1 + 2 = 3 - 边(1,3):深度(1) + 深度(2) = 1 + 2 = 3 - 边(2,4):深度(2) + 深度(3) = 2 + 3 = 5 - 边(3,5):深度(2) + 深度(3) = 2 + 3 = 5 总权重 = 3 + 3 + 3 + 0 = 9 因此,执行最佳重连操作后的最小总权重为9。 |
题解
问题分析
这个问题要求我们重新连接树中的一个子树,以最小化树的总权重。树的总权重定义为所有边上两个端点深度之和。要找到最优解,我们需要:
- 计算原始树的各个边的权重之和
- 对于每个非根节点,模拟将其与其父节点断开,并连接到其他节点的情况
- 找出能够最小化总权重的重连方案
解题思路
首先,我们需要理解一个关键点:树的总权重可以表示为 ,其中
是节点
的深度。
解题步骤如下:
- 使用深度优先搜索(DFS)计算每个节点的深度和子树大小
- 计算原始树的总权重
- 对于每个非根节点,计算如果将其子树重新连接到树中的其他节点,总权重的变化
- 选择能使总权重减少最多的重连方案
当我们将一个节点 的子树重新连接到节点
时,总权重的变化为:
- 子树中所有节点的深度变化量 × 2
- 其中深度变化量 = 新深度 - 旧深度 = depth(v) + 1 - depth(u)
因此,对于节点 的子树的每个节点,深度的总变化为:
总权重的变化为:
我们需要找到节点 ,使得
最小,同时避免
是
子树中的节点(这会导致环)。
复杂度分析
- 时间复杂度:
,其中
是节点数。我们需要进行一次DFS遍历计算深度和子树大小,然后再遍历所有节点寻找最优重连方案。
- 空间复杂度:
,用于存储树的结构和各节点的深度与子树大小。
参考代码
- Python
import sys
sys.setrecursionlimit(10**6)
input = lambda: sys.stdin.readline().strip()
def solve():
n = int(input()) # 节点数量
# 建立树的邻接表
tree = [[] for _ in range(n+1)]
for _ in range(n-1):
u, v = map(int, input().split())
tree[u].append(v)
tree[v].append(u)
depth = [0] * (n+1) # 节点深度
subtree_size = [0] * (n+1) # 子树大小
# DFS计算深度和子树大小
def dfs(node, parent, current_depth):
depth[node] = current_depth
subtree_size[node] = 1
for child in tree[node]:
if child != parent:
dfs(child, node, current_depth + 1)
subtree_size[node] += subtree_size[child]
# 从节点1开始DFS(假设1为根节点)
dfs(1, -1, 1)
# 计算原始树的总权重
total_weight = 0
for i in range(1, n+1):
total_weight += 2 * depth[i]
# 初始化父节点数组
parent = [-1] * (n+1)
for i in range(1, n+1):
for child in tree[i]:
if depth[child] == depth[i] + 1:
parent[child] = i
# 计算每个非根节点重连后的总权重变化
min_weight = total_weight
for u in range(2, n+1): # 从2开始,1是根节点
# 计算将u子树断开并重连到其他节点的权重变化
subtree_nodes = subtree_size[u]
# 模拟连接到每个可能的节点
for v in range(1, n+1):
# 跳过u自身、u的祖先节点和u的子树中的节点
if v == u or is_ancestor(parent, v, u):
continue
# 计算深度变化
depth_change = depth[v] + 1 - depth[u]
# 计算总权重变化
weight_change = 2 * subtree_nodes * depth_change
# 更新最小权重
min_weight = min(min_weight, total_weight + weight_change)
print(min_weight)
def is_ancestor(parent, ancestor, node):
"""检查ancestor是否为node的祖先节点"""
while node != -1:
if node == ancestor:
return True
node = parent[node]
return False
if __name__ == "__main__":
solve()
- Cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
vector<int> tree[MAXN];
int depth[MAXN];
int subtree_size[MAXN];
int parent[MAXN];
// DFS计算深度和子树大小
void dfs(int node, int par, int current_depth) {
depth[node] = current_depth;
subtree_size[node] = 1;
parent[node] = par;
for (int child : tree[node]) {
if (child != par) {
dfs(child, node, current_depth + 1);
subtree_size[node] += subtree_size[child];
}
}
}
// 检查ancestor是否为node的祖先节点
bool is_ancestor(int ancestor, int node) {
while (node != 0) {
if (node == ancestor)
return true;
node = parent[node];
}
return false;
}
int main
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力