1 条题解
-
0
📝 题目大意
给定一个长度为 的非负整数序列 ,从中选择 个元素(保持原顺序),求能得到的子序列 的 MEX 的最大值。MEX 定义为最小的不在序列中的非负整数。
💡 解题思路
-
题目分析:
- ,,数据范围较大,需要 或 的解法。
- MEX 只关心集合中有哪些数,与元素顺序无关,因此"保持原顺序"的约束实际上不影响结果。
- 要让 MEX 尽可能大,需要让 这 个数都出现在 中,每个数至少出现一次。由于只能选 个元素,所以 最大不超过 (每个数至少消耗一个名额)。
-
算法推导:
- 先将数组 排序(
std::sort),方便从小到大遍历。 - 维护变量
curt表示当前期望找到的数字(从 开始),k表示剩余可选元素个数,ans记录答案(初始为 )。 - 遍历排序后的数组:
- 若
k <= 0,说明名额用完了,无法继续扩展 MEX,跳出循环。 - 若
v[i] != curt,说明数字curt在原数组中不存在,MEX 最大只能到curt,记录答案并跳出。 - 若
v[i] == curt,说明找到了curt,消耗一个名额(k--),然后跳过所有等于curt的重复元素(while (i < n && v[i] == curt) i++),curt加一继续找下一个数。
- 若
- 若循环正常结束而
ans仍为 ,说明所有出现过的连续数字都已被收集,ans = curt。
- 先将数组 排序(
-
边界与细节:
- 当 耗尽时,即使后面的数字仍然连续,也无法再选,MEX 止步于当前
curt。 - 当数组中缺少某个中间数字(如缺少 但有 ),MEX 会卡在缺失的数字处,即使 还有剩余。
- 输入中 可能远大于 ,排序后可以直接忽略大于 的部分(因为 MEX 最大不超过 ),但代码中未做此优化也无妨,因为排序后遍历到
k <= 0就会break。 - 初始
ans = -1作为哨兵值,用于判断循环是否因找到缺失数字而提前退出。
- 当 耗尽时,即使后面的数字仍然连续,也无法再选,MEX 止步于当前
⏱️ 复杂度分析
- 时间复杂度:,排序占主导,后续遍历为 。
- 空间复杂度:,存储输入数组。
💻 标准代码 (C++)
#include <bits/stdc++.h> using namespace std; int n, k; int main() { scanf("%d%d", &n, &k); std::vector<int> v(n, 0); for (int i = 0; i < n; i++) { scanf("%d", &v[i]); } // 排序后从小到大处理,方便按顺序查找 0, 1, 2, ... std::sort(v.begin(), v.end()); int curt = 0; // 当前期望找到的数字 int i = 0; // 遍历指针 int ans = -1; // 哨兵:-1 表示尚未找到缺失数字 for (; i < n && ans == -1;) { if (k <= 0) { break; } // 名额用完,无法继续扩展 MEX if (v[i] != curt) { // 数字 curt 在原数组中不存在 ans = curt; // MEX 最大为 curt break; } k--; // 消耗一个名额选取 curt while (i < n && v[i] == curt) { // 跳过所有重复的 curt i++; } curt++; // 尝试找下一个数 } if (ans == -1) ans = curt; // 所有连续数字都找到了,MEX = curt printf("%d", ans); return 0; } -
- 1
信息
- ID
- 694
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者