牛客小白月赛115-B题:签到题
题目传送门牛客网竞赛题目
一、题目描述
给定n道题目,每道题难度为aᵢ。要从中选出m道题组成比赛,使得难度最低的题目(签到题)数量尽可能多。求签到题的最大可能数量。
输入:
- 第一行两个整数n,m(1≤m≤n≤2×10⁵)
- 第二行n个整数表示题目难度aᵢ(1≤aᵢ≤n)
示例1:
输入:
5 3
1 2 2 2 3
输出:
3
二、题目分析
我们需要从n道题中选m道,使得难度最低的题目尽可能多。关键在于找出m个数的窗口,然后把最低的一个数的数量找出最大值,借助后缀和数组s实现
三、解题思路
- 统计每个难度出现的次数
- map按照键值从小到大,这道题刚刚好满足我们要的
- 从最小难度开始检查:如果比当前难度大的题目总数+当前难度题目数≥m,则当前难度可以作为最低难度
四、算法讲解
- 统计各难度出现频率(使用map自动排序)
- 预处理后缀和数组s[i]表示难度≥vec[i]的题目总数
- 对于每个难度,检查能否作为最低难度:
- 若能,则最大数量为min(该难度题目数,m)
- 否则终止检查(后续难度更大不可能更优,因为后缀和的是数量)
五、代码实现
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 +10;
map<int, int> mp; // 统计各难度出现次数(自动按难度排序)
int n, m;
int s[N]; // 后缀和数组int main()
{cin >> n >> m;for (int i = 1; i <= n; i++){int x;cin >> x;mp[x]++; // 统计每个难度出现次数}// 将map转为vector便于处理vector<pair<int, int>> vec(mp.begin(), mp.end());int n = vec.size();// 计算后缀和:s[i]表示难度≥vec[i]的题目总数for (int i = n - 1; i >= 0; i--){s[i] = s[i + 1] + vec[i].second;}int ans = 0;for (int i = 0; i < n; i++){// 如果选择当前难度作为最低难度,是否能有足够题目if (s[i] >= m){// 最大数量不超过当前难度题目数,也不超过mans = max(ans, vec[i].second);}elsebreak; // 后续难度更大,不可能更优}cout << min(ans, m); // 最终结果不超过mreturn 0;
}
六、代码重点细节解释
map<int, int> mp
:自动按难度排序并统计频次- 后缀和数组
s[i]
:快速获取难度≥当前难度的题目总数 - 贪心检查:从最小难度开始,一旦发现无法满足条件立即终止
min(ans, m)
:确保结果不超过题目总数m
七、复杂度分析
- 时间复杂度:O(nlogn)(map插入和排序的复杂度)
- 空间复杂度:O(n)(存储频次和后缀和)