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
第一个测试用例:小柯从坐标 开始,执行指令 后到达 ,执行 后到达 ,执行 后到达 。最终位置是 ,不是宝藏位置 ,所以输出 "NO"。

第二个测试用例:小柯从坐标 开始,执行完所有指令 后,位置变化过程为:。最终位置恰好是宝藏位置 ,所以输出 "YES"。

题解

这道题目本质上是在二维平面上跟踪一个点的移动,并判断它是否能到达特定位置。

解题思路非常直观:

  1. 记录初始位置坐标
  2. 统计指令序列中每种方位指令的出现次数
  3. 根据各个方向的移动次数,计算最终位置
  4. 判断最终位置是否为

假设我们统计了以下数据:

  • 向北()移动了
  • 向南()移动了
  • 向东()移动了
  • 向西()移动了

那么最终的坐标变化为:

  • 轴方向的净变化 =
  • 轴方向的净变化 =

最终坐标 = 初始坐标 + 坐标变化

如果 ,则说明恰好到达了宝藏位置,输出 "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。

题解

问题分析

这个问题要求我们重新连接树中的一个子树,以最小化树的总权重。树的总权重定义为所有边上两个端点深度之和。要找到最优解,我们需要:

  1. 计算原始树的各个边的权重之和
  2. 对于每个非根节点,模拟将其与其父节点断开,并连接到其他节点的情况
  3. 找出能够最小化总权重的重连方案

解题思路

首先,我们需要理解一个关键点:树的总权重可以表示为 ,其中 是节点 的深度。

解题步骤如下:

  1. 使用深度优先搜索(DFS)计算每个节点的深度和子树大小
  2. 计算原始树的总权重
  3. 对于每个非根节点,计算如果将其子树重新连接到树中的其他节点,总权重的变化
  4. 选择能使总权重减少最多的重连方案

当我们将一个节点 的子树重新连接到节点 时,总权重的变化为:

  • 子树中所有节点的深度变化量 × 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%内容,订阅专栏后可继续查看/也可单篇购买

互联网刷题笔试宝典 文章被收录于专栏

互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

全部评论

相关推荐

评论
1
2
分享

创作者周榜

更多
牛客网
牛客企业服务