美团笔试 美团笔试题 0412
笔试时间:2025年04月12日
历史笔试传送门:
第一题
题目:数组1.0
给定一个长度为 n 的数组 a。定义一次操作: 选择数组中的一个数,然后把这个数从数组中移除。其余元素按照原有顺序从前到后依次拼接。现在她想要知道,自己最少需要操作几次,才能使得数组中所有非空子数组的平均值均相同。子数组为从原数组中,连续的选择一段元素(可以全选、可以不选)得到的新数组。
输入描述
第一行输入一个整数 n(1 <= n <= 2 * 10^5),表示数组长度。
第二行输入 n 个整数 a_1,a_2,.....,a_n \left(1 <= a_i <= 10^9 ) 代表数组。
输出描述
一个整数,表示最少操作次数。
样例输入
3
1 2 3
样例输出
2
说明:删除任意两个元素后,数组只剩下一个元素,此时只有一个非空子数组,一定满足题意。
参考题解
要使数组中所有非空子数组的平均值均相同,关键在于保证剩余元素满足特定条件。通过分析可知,唯一满足条件的剩余数组是所有元素相同或仅剩一个元素的情况。子数组特性: 若数组中存在不同元素,则子数组的平均值必然不同。例如,数组 [a, b] 的子数组平均值为 a、b、(a+b)/2,显然无法全部相同。 因此,只有剩余元素全相同或只剩一个元素时,才能满足所有子数组的平均值相同。操作目标:情况1:保留所有相同元素。此时需删除其他元素,操作次数为 n - max_count(max_count 是出现次数最多的元素的频次)。情况2:仅保留一个元素。操作次数为 n-1。 最终答案为两者的最小值 min(n - max_count, n-1),但进一步观察可知,当 max_count ≥ 1 时,n - max_count ≤ n-1,因此答案简化为 n - max_count。
C++:[此代码未进行大量数据的测试,仅供参考]
#include <iostream> #include <unordered_map> using namespace std; int main() { int n; cin >> n; unordered_map<int, int> cnt; int mx = 0; for (int i = 0; i < n; ++i) { int x; cin >> x; cnt[x]++; mx = max(mx, cnt[x]); } cout << n - mx << endl; return 0; }
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] a = new int[n]; Map<Integer, Integer> countMap = new HashMap<>(); int mx = 0; for (int i = 0; i < n; i++) { a[i] = sc.nextInt(); countMap.put(a[i], countMap.getOrDefault(a[i], 0) + 1); mx = Math.max(mx, countMap.get(a[i])); } System.out.println(n - mx); } }
Python:[此代码未进行大量数据的测试,仅供参考]
from collections import defaultdict n = int(input()) a = list(map(int, input().split())) cnt = defaultdict(int) mx = 0 for num in a: cnt[num] += 1 if cnt[num] > mx: mx = cnt[num] print(n - mx)
第二题
题目:小美的排列区间
小美拿到了一个数组 a,她用以下方式生成数组 b:初始b为空,随后,对于每个 a_i,依次将 1 到 a_i 这 a_i 个数添加到数组 b 的末尾。例如,对于a数组为[3,1,2],生成的b数组为:[1,2,3,1,1,2]。现在小美想知道,b数组中有多少个连续子数组为排列?连续子数组为从原数组中,连续的选择一段元素(可以全选、可以不选)得到的新数组。长度为 n 的排列是由 1 ~ n 这 n 个整数、按任意顺序组成的数组,其中每个整数恰好出现一次。例如,{2,3,1,5,4} 是一个长度为 5 的排列,而 {1,2,2} 和 {1,3,4} 都不是排列,因为前者存在重复元素,后者包含了超出范围的数。
输入描述
第一行输入一个正整数n,代表数组a的大小。
第二行输入n个正整数a_i,代表数组a的元素。
1 <= n <= 10^51 <= a_i <= 10^9
输出描述
一个整数,代表 b 数组的全部连续子数组中,为排列的数量。
样例输入
3
3 1 2
样例输出
7
说明:[1,2,3,1,1,2]有7个连续子数组是排列:3个长度为1的,2个长度为2的,2个长度为3的。
参考题解
数据比较大,不可能直接生成数组去统计,必须利用排列的特性和题目的特殊要求进行快速求解。块内贡献:每个块生成的子数组贡献的数量等于该块的长度,因为每个块的前m个元素恰好是1到m的排列。每个块生成的序列是[1,2,...,a_i]。这些序列的每个前缀子数组[1], [1,2], ..., [1,2,...,a_i] 都是排列。 例如,a_i=3 时贡献3个排列,a_i=1 时贡献1个。 总块内贡献是 sum_a = a[0] + a[1] + ... + a[n-1]。跨块贡献:对于每对相邻的块,计算可能形成排列的连续子数组数目。这些子数组必须满足最大值等于长度,并且元素总和为1到该长度的总和。当两个相邻块生成的序列连接时,可能形成跨越两个块的排列子数组。例如:前一个块的末尾部分是 [x, x+1, ..., a_i]后一个块的开头部分是 [1, 2, ..., y]组合后的子数组 [x, x+1, ..., a_i, 1, 2, ..., y] 可能是排列。子数组的长度必须等于其最大值。例如,长度为k的排列最大值是k。假设子数组跨越前块的末尾p个元素和后块的开头q个元素,总长度k = p + q。最大值只能是前块的末尾元素(即a_i),因此需满足 k = a_i。此时后块需要提供 q = a_i - p 个元素(且 q <= a_j)。p 的最小值:max(a_i - a_j, 1)p 的最大值:a_i - 1贡献数为 max(0, (a_i-1) - max(a_i - a_j, 1) + 1)。
C++:[此代码未进行大量数据的测试,仅供参考]
#include <iostream> #include <vector> using namespace std; int main() { int n; cin >> n; vector<long long> a(n); long long ans = 0; for (int i = 0; i < n; ++i) { cin >> a[i]; ans += a[i]; } for (int i = 0; i < n - 1; ++i) { long long a_i = a[i]; long long a_j = a[i + 1]; long long dn = max(a_i - a_j, 1LL); long long up = a_i - 1; if (dn <= up) { ans += up - dn + 1; } } cout << ans << endl; return 0; }
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] a = new int[n]; long ans = 0; for (int i = 0; i < n; i++) { a[i] = sc.nextInt(); ans += a[i]; } for (int i = 0; i < n - 1; i++) { int a_i = a[i]; int a_j = a[i + 1]; int dn = Math.max(a_i - a_j, 1); int up = a_i - 1; if (dn <= up) { ans += (up - dn + 1); } } System.out.println(ans); } }
Python:[此代码未进行大量数据的测试,仅供参考]
n = int(input
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南