【春招笔试】2025.05.10-美团春招笔试第二套改编真题

✅ 春招备战指南 ✅

💡 学习建议:

  • 先尝试独立解题(建议用时:90分钟/套)
  • 对照解析查漏补缺

🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。

🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力

🌸 目前本专栏已经上线70+套真题改编解析,后续会持续更新的

春秋招合集 -> 互联网必备刷题宝典🔗

题目一:星际旅行计划

1️⃣:统计数组中值为1的元素数量

2️⃣:计算曼哈顿距离并判断奇偶性关系

难度:中等

这道题目的关键在于理解移动的特性,值为0的元素让我们原地不动,值为1的元素允许在四个方向上移动一步。通过数学分析,我们可以推导出一个O(n)的高效解法,无需模拟实际移动过程。

题目二:图论专家的连通图分析

1️⃣:将条件转化为数组元素与索引差值的大小关系

2️⃣:计算前缀最大值和后缀最小值

3️⃣:寻找可切分的位置计算连通块数量

难度:中等

这道题目通过巧妙的数学变换,将复杂的图论问题转化为序列分析问题。利用转换后的数组特性,我们可以高效地找出断开连通性的位置,从而求出极大连通块的数量,时间复杂度为O(n)。

题目三:双递增子序列划分问题

1️⃣:利用Dilworth定理将问题转化为不存在长度为3的不增子序列

2️⃣:预处理每个位置的左右不增边界

3️⃣:使用扫描线算法和树状数组高效处理多次查询

难度:中等偏难

这道题目结合了经典的序列分解理论和高效的数据结构。通过理论分析,将问题转化为判断是否存在特定模式,然后设计离线算法高效处理多次查询,时间复杂度为O((n+q)logn)。

01. 星际旅行计划

问题描述

小柯是一名星际探险家,她正在一个无限广阔的二维宇宙中规划航行路线。初始时,她的飞船位于坐标

她的飞船能量系统由 个能量单元组成,每个能量单元的值为 。当小柯使用第 个能量单元时,她必须选择两个整数 ,满足 ,然后飞船会移动到新的位置

小柯想知道,在使用完所有 个能量单元后,她是否能恰好到达目标坐标

输入格式

第一行包含一个整数 ,表示测试用例的数量。

对于每个测试用例:

  • 第一行包含一个整数 ,表示能量单元的数量。
  • 第二行包含 个整数,第 个整数为 ,表示每个能量单元的值。
  • 第三行包含四个整数 ,分别表示起始坐标和目标坐标。

数据保证单个测试文件中

输出格式

对于每个测试用例输出一行,如果小柯能够恰好到达目标坐标 ,则输出 "YES",否则输出 "NO"。

样例输入

2
2
0 0
1 1 1 1
3 
1 1 1
1 1 2 2

样例输出

YES
NO
样例 解释说明
样例1 在这个例子中,小柯有两个值为0的能量单元。使用第一个能量单元,她可以选择 原地不动;使用第二个能量单元,仍选择 。最终她仍停留在初始位置 ,恰好是目标坐标。
样例2 小柯有三个值为1的能量单元。无论如何选择,她无法从 正好到达

数据范围

  • 所有测试用例中 的总和不超过

题解

这道题目看似复杂,但实际上有一个很直观的解法。让我们分析一下:

首先,我们需要理解能量单元的使用方式。当 时,我们只能选择 ,即原地不动。当 时,我们有四种选择:,相当于在四个方向上移动一步。

所以,我们的移动能力受到两个因素限制:

  1. 总共能移动多少步(由 的数量决定)
  2. 每一步只能在四个正交方向上移动

从起点 到目标点 的最短曼哈顿距离是 。这意味着我们至少需要这么多步才能到达目标。

此外,每次移动都会改变奇偶性。考虑到这一点,能否到达目标还需满足一个条件:如果总移动步数减去最短距离后的剩余步数是奇数,那么我们永远无法到达目标。因为每多走两步才能回到同一个奇偶性的位置。

综合这两个条件,我们可以得出解题的关键判断:

  • 为数组中值为1的元素数量(即可移动的步数)
  • , 为横纵坐标的距离
  • 当且仅当 时,才能到达目标

时间复杂度:,我们只需要遍历一次数组来统计值为1的元素数量。 空间复杂度:,只需要常数额外空间。

参考代码

  • Python
import sys
input = lambda:sys.stdin.readline().strip()

def solve():
    t = int(input())
    for _ in range(t):
        n = int(input())
        a = list(map(int, input().split()))
        x, y, p, q = map(int, input().split())
        
        # 统计可移动的步数
        ones = sum(1 for val in a if val == 1)
        
        # 计算目标距离
        dx = abs(p - x)
        dy = abs(q - y)
        
        # 判断是否可达
        if ones >= dx + dy and (ones - (dx + dy)) % 2 == 0:
            print("YES")
        else:
            print("NO")

if __name__ == "__main__":
    solve()
  • Cpp
#include <iostream>
#include <cmath>
using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        
        ll ones = 0;
        for (int i = 0; i < n; i++) {
            int a;
            cin >> a;
            ones += (a == 1);
        }
        
        ll x, y, p, q;
        cin >> x >> y >> p >> q;
        
        ll dx = abs(p - x);
        ll dy = abs(q - y);
        
        if (ones >= dx + dy && (ones - (dx + dy)) % 2 == 0)
            cout << "YES\n";
        else
            cout << "NO\n";
    }
    return 0;
}
  • Java
import java.io.*;
import java.util.*;

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().trim());
        
        while (t-- > 0) {
            int n = Integer.parseInt(br.readLine().trim());
            String[] vals = br.readLine().trim().split(" ");
            
            long ones = 0;
            for (int i = 0; i < n; i++) {
                if (vals[i].equals("1")) ones++;
            }
            
            String[] coords = br.readLine().trim().split(" ");
            long x = Long.parseLong(coords[0]);
            long y = Long.parseLong(coords[1]);
            long p = Long.parseLong(coords[2]);
            long q = Long.parseLong(coords[3]);
            
            long dx = Math.abs(p - x);
            long dy = Math.abs(q - y);
            
            if (ones >= dx + dy && (ones - (dx + dy)) % 2 == 0)
                System.out.println("YES");
            else
                System.out.println("NO");
        }
    }
}

02. 图论专家的连通图分析

问题描述

小毛是一位图论爱好者,他正在研究一种特殊的无向图构建方法。他有一个长度为 的数组 ,他定义一对数字 是"和谐对"当且仅当:

小毛构建图的规则是:对于任意一对索引 ),如果 是和谐对,则在点 和点 之间连接一条无向边。

现在,小毛想知道他所构建的图中有多少个极大连通块。由于图中的边可能非常多,他希望你能帮他计算。

(注:对于图上的两个点,如果它们之间有边相连或者可以通过一系列边间接相连,则称它们位于同一个连通块中。对于一个连通块,如果无法再加入更多的点,则称其为极大连通块。)

输入格式

第一行包含一个整数 ,表示测试用例的数量。

对于每个测试用例:

  • 第一行包含一个整数 ,表示数组 的长度。
  • 第二行包含 个正整数 ,表示数组 的元素。

数据保证单个测试文件中所有 的总和不超过

输出格式

对于每个测试用例输出一行,包含一个整数,表示图中极大连通块的数量。

样例输入

2
4
3 3 4 6
5
1 2 3 4 5

样例输出

2
5
样例 解释说明
样例1 对于数组 ,构建的图有两个极大连通块。根据和谐对的定义,点 1 和点 3、点 1 和点 4、点 2 和点 3、点 2 和点 4 之间有边,形成一个连通块;而点 3 和点 4 之间没有边,单独形成另一个连通块。
样例2 对于数组 ,每个点都是一个独立的连通块,因为没有任何点对满足和谐对的定义。

数据范围

  • 所有测试用例中 的总和不超过

题解

这道题目看似复杂,但通过数学变换可以找到一个优雅的解法。

首先,分析一下"和谐对"的条件:

我们可以将其变形为:

定义一个新的数组 ,其中 ,则条件变为:(当 时)

这意味着,我们在所有形成"逆序对"的位置 之间连边。这种特殊的图结构的连通块数量等价于将序列 切分为若干段,使得每一段内部存在逆序对,而不同段之间没有逆序对。

当某个位置 满足以下条件时,可以在 之间"断开"连通性:

最终的连通块数就是这样的切分段数。

我们可以通过以下步骤求解:

  1. 计算后缀最小值
  2. 从左到右遍历,维护前缀最大值
  3. 每当 时,就可以在位置 之间切分,连通块数量加一

时间复杂度:,我们只需要两次遍历数组 空间复杂度:,需要额外存储转换后的数组和后缀最小值

参考代码

  • Python
import sys
input = lambda:sys.stdin.readline().strip()

def solve():
    t = int(input())
    for _ in range(t):
        n = int(input())
        a = [0] + list(map(int, input().split()))
        
        # 计算b[i] = a[i] - i
        b = [0] * (n + 1)
        for i in range(1, n + 1):
            b[i] = a[i] - i
        
        # 计算后缀最小值
        suffix_min = [float('inf')] * (n + 2)
        for i in range(n, 0, -1):
            suffix_min[i] = min(b[i], suffix_min[i + 1])
        
        # 扫描切分
        prefix_max = b[1]
        comps = 1  # 至少有一个连通块
        
        for i in range(1, n):
            prefix_max = max(prefix_max, b[i])
            if prefix_max <= suffix_min[i + 1]:
                comps += 1
        
        print(comps)

if __name__ == "__main__":
    solve()
  • Cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        
        // 读取数组并计算b[i] = a[i] - i
        vector<ll> b(n + 1);
        for (int i = 1; i <= n; i++) {
            ll a;
            cin >> a;
            b[i] = a - i;
        }
        
        // 计算后缀最小值
        vector<ll> suffix_min(n + 2, LLONG_MAX);
        for (int i = n; i >= 1; i--) {
            suffix_min[i] = min(b[i], suffix_min[i + 1]);
        }
        
        // 扫描切分
        ll prefix_max = b[1];
        int comps = 1;  // 至少有一个连通块
        
        for (int i = 1; i < n; i++) {
            prefix_max = max(prefix_max, b[i]);
            if (prefix_max <= suffix_min[i + 1]) {
                comps++;
            }
        }
        
        cout << comps << "\n";
    }
    
    return 0;
}
  • Java
import java.io.*;
import java.util.*;

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().trim());
        
        while (t-- > 0) {
            int n = Integer.parseInt

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

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

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

全部评论

相关推荐

虽然笔试没过,但还是参加一下AI面试#牛客AI配图神器#,以下是记录总结。#美团AI面试#1.计算机网络:TCP的粘包问题(刚背的,可熟悉了)回答:在网络拥塞或者Nagle算法中,会将多个TCP包合在一起,但是TCP在传输中是以字节流形式的,粘在一起之后没办法区分包之间的边界,例如发送两个数据包Hello&nbsp;和World,合在一起后,接收方收到的是HelloWorld。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;解决办法主要有四个,第一个是固定数据包的大小,发生粘包后根据字节大小切分,但这样比较死板;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第二种是在数据包末尾加入&quot;\n&quot;的分隔符,但是在中间文本中出现了分隔符的话,需要进行转义;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第三种是在TCP报文前加入一个字段,记录这个包的大小,这种比较常用;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第四种是对协议进行限制,例如禁用Nagle算法,不允许合并数据包。2.Linux:grep如何使用(确实没准备)回答:完全不会。3.Java:装箱和拆箱是什么,举一个例子。回答:举了int型和Integer类的例子,一些接口使用的时候会发生拆箱和装箱(但我不太记得了)4.高并发:解决投票中刷票问题回答:使用IP检测,限制单个IP的高频访问。追问:要是对方使用代理IP,或者IP连接池该怎么解答回答:根据行为检测,刷票行为和个人投票行为肯定存在差异(但我不知道差异是什么)5.数据库:主键的作用是检索到这条数据的唯一标识,也用来构建聚簇索引。(应该还有其他作用但我忘了。)6.通用问题1:学习新技术的路线回答:阅读官方文档,对于难点搜索视频讲解或者Github文档;项目实战有问题去找熟悉项目的技术人员。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;追问1:如何判断学习资料是否有用,是否走过弯路&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;回答:根据点赞量,Start量。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;追问2:走过弯路后下次如何判断?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;回答:根据文档关键词判断对我是否有帮助?7.通用问题2:你项目中遇到的难点怎么解决追问1:如何通过持续改进解决一个一直出现的问题追问2:如何平衡局部调优和整体的稳定性8.通用问题3:你未来3年的职业规划回答:1-2年熟悉公司技术栈,后续熟悉公司业务流程,做到技术和业务两手抓。最后给出DeepSeek的回答。1.计算机网络:TCP的粘包问题 &nbsp;TCP粘包问题是由于TCP是面向字节流的协议,发送端可能将多个数据包合并发送(如Nagle算法或网络拥塞),接收端无法区分数据边界。例如发送&quot;Hello&quot;和&quot;World&quot;,接收端可能收到&quot;HelloWorld&quot;。&nbsp;解决方案&nbsp;:●&nbsp;固定包长度&nbsp;:每个包固定为相同长度(不足时填充),接收方按固定长度拆分。缺点是灵活性差,适合固定格式场景。●&nbsp;分隔符&nbsp;:在数据包末尾添加特殊字符(如\n),接收方按分隔符拆分。需处理数据中的转义字符(如HTTP头用\r\n\r\n)。●&nbsp;头部声明长度&nbsp;:在报文头部添加长度字段(如4字节表示包体长度),接收方先读头部,再按长度截取数据。这是最常用的方式(如HTTP的Content-Length)。●&nbsp;禁用Nagle算法&nbsp;:通过TCP_NODELAY选项关闭合并优化,但可能增加小包传输次数,影响性能。2.Linux:grep如何使用 grep用于文本搜索,常见用法:●&nbsp;基本搜索&nbsp;:grep&nbsp;&quot;pattern&quot;&nbsp;file.txt●&nbsp;递归搜索目录&nbsp;:grep&nbsp;-r&nbsp;&quot;pattern&quot;&nbsp;/path/to/dir●&nbsp;忽略大小写&nbsp;:grep&nbsp;-i&nbsp;&quot;pattern&quot;&nbsp;file.txt●&nbsp;显示行号&nbsp;:grep&nbsp;-n&nbsp;&quot;pattern&quot;&nbsp;file.txt●&nbsp;反向匹配(排除)&nbsp;&nbsp;:grep&nbsp;-v&nbsp;&quot;pattern&quot;&nbsp;file.txt●&nbsp;正则表达式&nbsp;:支持^(行首)、$(行尾)、.(任意字符)等,如grep&nbsp;&quot;^start&quot;&nbsp;file.txt。3.Java:装箱和拆箱是什么,举一个例子 ●&nbsp;装箱(Autoboxing)&nbsp;&nbsp;:将基本类型转换为包装类,如int&nbsp;→&nbsp;Integer。●&nbsp;拆箱(Unboxing)&nbsp;&nbsp;:将包装类转换为基本类型,如Integer&nbsp;→&nbsp;int。Integer&nbsp;a&nbsp;=&nbsp;10;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;自动装箱(底层调用Integer.valueOf(10))&nbsp;&nbsp;int&nbsp;b&nbsp;=&nbsp;a;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;自动拆箱(底层调用a.intValue())&nbsp;&nbsp;4.&nbsp;高并发:解决投票中刷票问题&nbsp;●&nbsp;基础防御&nbsp;:&nbsp;&nbsp;IP限流&nbsp;:限制同一IP单位时间内的请求次数(如Redis计数器+过期时间)。○&nbsp;用户鉴权&nbsp;:要求登录后投票,限制每个用户的投票次数。○&nbsp;验证码&nbsp;:在频繁请求时触发验证码(如滑动拼图、短信验证)。●&nbsp;对抗代理/IP池&nbsp;:&nbsp;&nbsp;设备指纹&nbsp;:通过浏览器指纹(UserAgent、Canvas渲染)、设备ID等标识唯一设备。○&nbsp;行为分析&nbsp;:检测异常行为(如毫秒级连续投票、无页面停留直接提交)。○&nbsp;风控系统&nbsp;:实时分析请求特征(IP地理分布、时间分布),拦截异常流量。●&nbsp;业务层优化&nbsp;:&nbsp;&nbsp;异步队列削峰&nbsp;:将投票请求放入消息队列,后端批量处理,避免瞬时高并发。5.&nbsp;&nbsp;数据库:主键的作用&nbsp;●&nbsp;唯一标识&nbsp;:确保每行数据的唯一性(如自增ID、UUID)。●&nbsp;聚簇索引&nbsp;:InnoDB中主键默认作为聚簇索引,数据按主键顺序物理存储,加快检索速度。●&nbsp;外键关联&nbsp;:主键作为其他表的外键,维护数据一致性。●&nbsp;优化查询&nbsp;:主键查询最快(如WHERE&nbsp;id=1),避免全表扫描。●注意事项&nbsp;:避免业务字段做主键(如身份证号),优先使用无意义的自增ID。●复合主键需谨慎,可能增加索引复杂度。6.&nbsp;&nbsp;通用问题1:学习新技术的路线&nbsp;●&nbsp;学习路径&nbsp;:&nbsp;&nbsp;官方文档&nbsp;:掌握核心概念和最佳实践(如Spring&nbsp;Boot的Spring.io)。&nbsp;&nbsp;&nbsp;&nbsp;○&nbsp;实践项目&nbsp;:通过小项目验证技术点(如用Redis实现分布式锁)。&nbsp;&nbsp;&nbsp;&nbsp;○&nbsp;源码与社区&nbsp;:阅读源码、参与GitHub讨论(如Dubbo的Issue)。●&nbsp;判断资料质量&nbsp;:&nbsp;&nbsp;筛选标准&nbsp;:作者权威性(如Oracle认证工程师)、内容更新日期、社区评价(Stack&nbsp;Overflow投票)。&nbsp;&nbsp;&nbsp;&nbsp;○&nbsp;避免弯路&nbsp;:优先选择官方推荐资源,再参考高星GitHub项目或经典书籍(如《Effective&nbsp;Java》)。●&nbsp;持续改进&nbsp;:&nbsp;定期复盘学习效果,建立知识体系脑图,与团队分享经验。7.&nbsp;&nbsp;通用问题2:项目难点解决&nbsp;&nbsp;结构化回答(STAR模型)&nbsp;&nbsp;:●&nbsp;情境(Situation)&nbsp;&nbsp;:在XX项目中,接口响应慢,导致超时报警。●&nbsp;任务(Task)&nbsp;&nbsp;:需在1周内将平均响应时间从2s优化至200ms内。●&nbsp;行动(Action)&nbsp;&nbsp;:&nbsp;&nbsp;定位瓶颈&nbsp;:通过Arthas追踪调用链,发现SQL查询耗时占80%。○&nbsp;优化SQL&nbsp;:添加索引、重构分页查询,减少全表扫描。○&nbsp;缓存引入&nbsp;:对热点数据(如配置信息)增加Redis缓存。●&nbsp;结果(Result)&nbsp;&nbsp;:响应时间降至150ms,且通过压测验证稳定性。●平衡调优与稳定&nbsp;:局部优化前评估影响范围,通过灰度发布观察效果。●监控核心指标(CPU、内存、错误率),确保优化不引发链式故障。8.&nbsp;&nbsp;通用问题3:未来3年的职业规划&nbsp;●&nbsp;短期(1年)&nbsp;&nbsp;:&nbsp;深入掌握公司技术栈(如微服务架构、中间件),成为团队核心开发。○参与高复杂度模块(如分布式事务、性能优化)。●&nbsp;中期(2年)&nbsp;&nbsp;:&nbsp;提升架构设计能力,主导跨系统方案设计(如亿级流量系统)。○培养业务洞察力,推动技术与业务融合(如通过数据驱动产品决策)。●&nbsp;长期(3年)&nbsp;&nbsp;:&nbsp;向技术管理者或架构师转型,负责技术选型与团队规划。○持续学习行业前沿(如云原生、AI工程化),保持技术敏感度。
美团AI面330人在聊 查看12道真题和解析
点赞 评论 收藏
分享
评论
1
2
分享

创作者周榜

更多
牛客网
牛客企业服务