✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题(建议用时:90分钟/套)
- 对照解析查漏补缺
- 配套练习题库
互联网必备刷题宝典🔗
📢 美团技术岗笔试重要信息速览
⏰ 笔试时间安排
- 常规场次:每周六交替进行
- 上午场 10:00~11:30
- 晚间场 19:00~20:30
- 通知时间:每周四/五通过邮箱发送考试链接
🧩 笔试题型分布
岗位类型 | 题目构成 |
---|---|
算法岗 | 选择题 + 5道编程 |
后端开发岗 | 选择题 + 3道编程 |
前端/测试岗 | 选择题 + 2道编程 |
⚙️ 考试设置要点
- 考试平台:牛客网(ACM模式)
- 监考要求:
- 必须开启笔记本前置摄像头
- 禁止使用手机(需小程序锁定)
- 允许使用本地IDE
- 编程规范:
- 严格遵循输入输出格式
- 注意时间复杂度控制(通常1s对应1e8次运算)
📚 笔试经验贴
(所有展示题面均已进行改编处理,保留核心考点)
本题库收录整理自:
- 互联网公开的笔试真题回忆版(经网友投稿)
- 各大技术社区公开讨论的经典题型
- 历年校招考生提供的解题思路
🔍 题库特点:
- 100%真实笔试场景还原
- 包含高频考点题型
- 提供多语言实现参考
- 持续更新2024届最新真题
⚠️ 注意事项:
- 所有题目均来自公开渠道,已进行改编脱敏处理
- 实际笔试可能出现题型变化,请以官方通知为准
🚀 春招备战指南
金三银四求职季即将到来!这里整理了最新美团真题及解析,助你快速掌握笔试套路。建议重点突破以下题型:
- 数组/字符串操作
- 树形结构应用
- 贪心/动态规划
- 区间合并问题
(👇 下附最新笔试真题及详细解析 👇)
真题详解(改编版)
第一题:数组变换
题目内容
小基有一个长度为 n n n 的数组,他每次操作会执行如下:
选定一个 a i a_i ai,把这个数加上一个任意的 x x x( x > 0 x>0 x>0),花费的代价为 a i + x a_i+x ai+x。
现在小基想要把整个数组变成全部奇数或者全部偶数的最小代价是多少?
输入描述
第一行一个整数 n n n( 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1≤n≤105)表示数组长度。
第二行 n n n 个整数,第 i i i 个数为 a i a_i ai( 1 ≤ a i ≤ 1 0 9 1≤a_i≤10^9 1≤ai≤109)表示数组元素。
输出描述
一个整数,表示小基想要把整个数组变成全部奇数或者全部偶数的最小代价。
样例1
输入:
3
1 2 3
输出:
3
说明:选择第 2 2 2 个数 + 1 +1 +1,数组变成 133 1 3 3 133。满足条件,代价是 2 + 1 = 3 2+1=3 2+1=3。
题解
这道题的关键是理解每个数字变换的代价。对于一个数字:
- 偶数变奇数的最小代价为 x + 1 x + 1 x+1
- 奇数变偶数的最小代价也是 x + 1 x + 1 x+1
只需要枚举两种情况:
- 全部变成奇数的代价
- 全部变成偶数的代价
取两者的最小值即为答案。
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组长度。
代码参考
- C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int len;
cin >> len;
vector<int> nums(len);
for (int i = 0; i < len; i++) {
cin >> nums[i];
}
long long cost[2] = {0, 0};
for (int val : nums) {
if (val % 2 == 0) {
cost[0] += val + 1;
} else {
cost[1] += val + 1;
}
}
cout << min(cost[0], cost[1]) << endl;
return 0;
}
- Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int size = scan.nextInt();
int[] arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = scan.nextInt();
}
long[] res = {0, 0};
for (int num : arr) {
if (num % 2 == 0) {
res[0] += num + 1;
} else {
res[1] += num + 1;
}
}
System.out.println(Math.min(res[0], res[1]));
}
}
- Python
n = int(input())
nums = list(map(int, input().split()))
cost = [0, 0]
for x in nums:
if x % 2 == 0:
cost[0] += x + 1
else:
cost[1] += x + 1
print(min(cost))
第二题:相似节点
题目内容
小基对于给定的由 n n n 个节点构成,根节点为 1 1 1 的有根树中,定义节点 u u u 和 v v v 是"相似节点",当且仅当节点 u u u 的子点数量 s o n u son_u sonu 与节点 v v v 的子点数量 s o n v son_v sonv 相等。
输出"相似节点"的对数。
输入描述
每个测试文件均含多组测试数据。第一行输入一个整数 T T T( 1 ≤ T ≤ 1 0 4 1≤T≤10^4 1≤T≤104)代表数据组数,每组测试数据描述如下:
第一行输入一个整数 n n n( 1 ≤ n ≤ 100 1≤n≤100 1≤n≤100)代表节点数量。
此后 n − 1 n-1 n−1 行,第 i i i 行输入两个整数 u i u_i ui 和 v i v_i vi( 1 ≤ u i , v i ≤ n ; u i ≠ v i 1≤u_i,v_i≤n;u_i≠v_i 1≤ui,vi≤n;ui=vi)表示树上第 i i i 条边连接节点 u i u_i ui 和 v i v_i vi。保证树联通,没有重边。
除此之外,保证所有的 n n n 之和不超过 2 × 1 0 5 2×10^5 2×105。
输出描述
对于每一组测试数据,在一行上输出一个整数,代表图中"相似节点"的对数。
样例1
输入:
1
7
1 2
1 3
3 5
3 7
2 4
2 6
输出:
9
题解
这道题的关键是统计每个节点的子节点数量,然后找出具有相同子节点数量的节点对。
解题步骤:
- 建立树的邻接表表示
- 统计每个节点的子节点数量
- 用哈希表记录相同子节点数量的节点个数
- 对于每个子节点数量,计算组合数 C(m,2),即 m*(m-1)/2
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是节点数量。
代码参考
- C++
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<vector<int>> g(n);
for (int i = 0; i < n-1; i++) {
int u, v;
cin >> u >> v;
g[u-1].push_back(v-1);
g[v-1].push_back(u-1);
}
unordered_map<int, int> cnt;
for (int u = 0; u < n; u++) {
int deg = g[u].size() - 1;
if (u == 0) deg++;
cnt[deg]++;
}
long long ans = 0;
for (auto [k, v] : cnt) {
ans += 1LL * v * (v-1) / 2;
}
cout << ans << endl;
}
return 0;
}
- Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int t = scan.nextInt();
while (t-- > 0) {
int n = scan.nextInt();
List<List<Integer>> tree = new ArrayList<>();
for (int i = 0; i < n; i++) {
tree.add(new ArrayList<>());
}
for (int i = 0; i < n-1; i++) {
int u = scan.nextInt() - 1;
int v = scan.nextInt() - 1;
tree.get(u).add(v);
tree.get(v).add(u);
}
Map<Integer, Integer> cnt = new HashMap<>();
for (int u = 0; u < n; u++) {
int deg = tree.get(u).size() - 1;
if (u == 0) deg++;
cnt.put(deg, cnt.getOrDefault(deg, 0) + 1);
}
long ans = 0;
for (int v : cnt.values()) {
ans += (long)v * (v-1) / 2;
}
System.out.println(ans);
}
}
}
- Python
def solve():
n = int(input())
tree = [[] for _ in range(n)]
for _ in range(n-1):
u, v = map(lambda x: int(x)-1, input().split())
tree[u].append(v)
tree[v].append(u)
cnt = {}
for u in range(n):
deg = len(tree[u]) - 1
if u == 0:
deg += 1
cnt[deg] = cnt.get(deg, 0) + 1
ans = 0
for v in cnt.values():
ans += v * (v-1) // 2
return ans
t = int(input())
for _ in range(t):
print(solve())
第三题:卡牌游戏
题目内容
小基和小柯在玩一个卡牌游戏,初始时桌面上有 n n n 种卡牌,每种卡牌有 a i a_i ai 张,这些牌都是背面朝上的。玩家操作时会先翻一张牌,然后再翻一张牌,若两张牌的类型相同,则玩家获胜,否则,重新将两张牌翻回背面朝上,两个玩家轮流操作。
小基和小柯总共会玩 q + 1 q+1 q+1 轮游戏。第 1 1 1 轮的卡牌数量为初始数量,后续每一轮会在上一轮游戏的基础上,增加或减少一些卡牌,然后将所有卡牌翻至背面朝上并重新打乱。
增加卡牌的操作为:$+l \ r\ x\ $表示第
l
l
l 种牌到第
r
r
r 种牌各增加
x
x
x 张。
减少卡牌的操作为:$-l \ r\ x\ $表示第
l
l
l 种牌到第
r
r
r 种牌各减少
x
x
x 张。
每一轮都是由小基先手,小基想让小柯获胜,小基想知道他至少需要偷看多少张牌才能保证小柯一定能获得胜利?若无法保证小柯一定获得胜利,则输出 − 1 -1 −1。
输入描述
第一行输入一个整数 q q q( 1 ≤ q ≤ 1 0 5 1≤q≤10^5 1≤q≤105)代表游戏轮数。
第二行输入 n n n 个整数 a i a_i ai( 0 ≤ a i ≤ 1 0 9 0≤a_i≤10^9 0≤ai≤109)表示数组。
接下来 q q q 行,每行先输入 1 1 1 个字符 c c c( c ∈ c∈ c∈{ ′ − ′ , ′ + ′ '-','+' ′−′,′+′}),再输入 3 3 3 个数字 l , r , x l,r,x l,r,x( 1 ≤ l ≤ r ≤ n 1≤l≤r≤n 1≤l≤r≤n), x x x( 1 ≤ x ≤ 1 0 9 1≤x≤10^9 1≤x≤109),表示操作。
输出描述
输出 q + 1 q+1 q+1 行,每行输出一个整数表示答案。
样例1
输入:
3
3 3
3 3
2 1
输出:
-1
1
题解
这道题的关键是理解如何让后手获胜。对于每个局面:
- 如果最多的牌数量为 mx:
- 小柯至少需要将所有的 mx 张牌翻出来
- 特殊情况:如果除了最多的牌外其他都只有1张,只需要 mx-1 次
- 如果最多的牌数量 mx ≤ 1,则不可能获胜,输出 -1
时间复杂度: O ( q log n ) O(q \log n) O(qlogn),其中 q q q 是询问次数, n n n 是牌的种类数。
代码参考
- C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int q;
cin >> q;
vector<long long> cards;
while (q--) {
int n;
cin >> n;
cards.resize(n);
for (int i = 0; i < n; i++) {
cin >> cards[i];
}
long long mx = *max_element(cards.begin(), cards.end());
int cnt = count(cards.begin(), cards.end(), 1);
if (mx <= 1) {
cout << -1 << endl;
} else if (cnt == n - 1) {
cout << mx - 1 << endl;
} else {
cout << mx << endl;
}
char op;
int l, r, x;
cin >> op >> l >> r >> x;
for (int i = l-1; i < r; i++) {
cards[i] += (op == '+' ? x : -x);
}
}
return 0;
}
- Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int q = scan.nextInt();
while (q-- > 0) {
int n = scan.nextInt();
long[] cards = new long[n];
for (int i = 0; i < n; i++) {
cards[i] = scan.nextLong();
}
long maxVal = 0;
int ones = 0;
for (long val : cards) {
maxVal = Math.max(maxVal, val);
if (val == 1) ones++;
}
if (maxVal <= 1) {
System.out.println(-1);
} else if (ones == n - 1) {
System.out.println(maxVal - 1);
} else {
System.out.println(maxVal);
}
String op = scan.next();
int l = scan.nextInt() - 1;
int r = scan.nextInt() - 1;
int x = scan.nextInt();
for (int i = l; i <= r; i++) {
cards[i] += op.equals("+") ? x : -x;
}
}
}
}
- Python
q = int(input())
for _ in range(q):
n = int(input())
cards = list(map(int, input().split()))
max_val = max(cards)
ones = sum(1 for x in cards if x == 1)
if max_val <= 1:
print(-1)
elif ones == n - 1:
print(max_val - 1)
else:
print(max_val)
if _ < q:
op, l, r, x = input().split()
l, r, x = map(int, [l, r, x])
for i in range(l-1, r):
cards[i] += x if op == '+' else -x
第四题:无限序列
题目内容
小基有一个无限长的正整数序列 [ 1 , 2 , 3 , . . . ] [1,2,3,...] [1,2,3,...],现在有 q q q 次询问,每次询问给出两个数字 x x x 和 y y y。
对于每一次询问,小基会执行 y y y 次删除操作,然后输出最后一个被删除的数字。
删除操作是指,找到第一个大于等于 x x x 的数字,然后将其从序列中永久删除。
输入描述
第一行输入一个整数 q q q( 1 ≤ q ≤ 1 0 5 1≤q≤10^5 1≤q≤105)代表询问次数。
接下来 q q q 行,每行输入两个整数 x , y x,y x,y( 1 ≤ x , y ≤ 1 0 9 1≤x,y≤10^9 1≤x,y≤109),其含义已在题目中说明。
输出描述
对于每一个询问,在一行上输出一个整数,代表在这次操作中最后被删除的那个数字。
样例1
输入:
3
3 3
3 3
2 1
输出:
5
8
2
说明:
序列初始为
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
.
.
.
]
[1,2,3,4,5,6,7,8,9,...]
[1,2,3,4,5,6,7,8,9,...]
- 第 1 次询问,依次删除 3 , 4 , 5 3,4,5 3,4,5,序列变为 [ 1 , 2 , 6 , 7 , 8 , 9 , . . . ] [1,2,6,7,8,9,...] [1,2,6,7,8,9,...]
- 第 2 次询问,依次删除 6 , 7 , 8 6,7,8 6,7,8,序列变为 [ 1 , 2 , 9 , . . . ] [1,2,9,...] [1,2,9,...]
- 第 3 次询问,删除 2 2 2,序列变为 [ 1 , 9 , . . . ] [1,9,...] [1,9,...]
题解
这是一个区间问题。对于每个询问:
- 每次删除操作会形成一个区间 [ x , x + y − 1 ] [x, x+y-1] [x,x+y−1]
- 如果 x 在已删除区间内,需要向右移动直到找到未删除的位置
- 如果删除区间的右端点在其他已删除区间内,需要合并区间
使用有序映射(TreeMap)维护已删除的区间,可以高效查找和合并区间。
时间复杂度: O ( q log q ) O(q \log q) O(qlogq),其中 q q q 是询问次数。
代码参考
- C++
#include <iostream>
#include <map>
using namespace std;
int main() {
int q;
cin >> q;
map<long long, long long> segs;
while (q--) {
long long x, y;
cin >> x >> y;
auto it = segs.upper_bound(x);
if (it != segs.begin()) {
it--;
if (it->second >= x) {
x = it->second + 1;
}
}
it = segs.upper_bound(x);
while (it != segs.end() && it->first <= x + y - 1) {
y += it->second - it->first + 1;
it = segs.erase(it);
}
segs[x] = x + y - 1;
cout << x + y - 1 << endl;
}
return 0;
}
- Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int q = scan.nextInt();
TreeMap<Long, Long> segs = new TreeMap<>();
while (q-- > 0) {
long x = scan.nextLong();
long y = scan.nextLong();
Map.Entry<Long, Long> prev = segs.floorEntry(x);
while (prev != null && prev.getValue() >= x) {
x = prev.getValue() + 1;
prev = segs.floorEntry(x);
}
Map.Entry<Long, Long> next = segs.higherEntry(x);
while (next != null && next.getKey() <= x + y - 1) {
y += next.getValue() - next.getKey() + 1;
segs.remove(next.getKey());
next = segs.higherEntry(x);
}
segs.put(x, x + y - 1);
System.out.println(x + y - 1);
}
}
}
- Python
from collections import defaultdict
q = int(input())
segs = {}
for _ in range(q):
x, y = map(int, input().split())
# 找到第一个未被删除的位置
l = max((k for k in segs if k <= x), default=None)
while l is not None and segs[l] >= x:
x = segs[l] + 1
l = max((k for k in segs if k <= x), default=None)
# 合并重叠区间
r = min((k for k in segs if k > x), default=None)
while r is not None and r <= x + y - 1:
tmp = segs[r]
del segs[r]
y += tmp - r + 1
r = min((k for k in segs if k > r), default=None)
segs[x] = x + y - 1
print(x + y - 1)