链接:https://www.nowcoder.com/acm/contest/142/G
来源:牛客网
Maximum Mode
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
The mode of an integer sequence is the value that appears most often. Chiaki has n integers a1,a2,...,an. She woud like to delete exactly m of them such that: the rest integers have only one mode and the mode is maximum.
输入描述:
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case: The first line contains two integers n and m (1 ≤ n ≤ 105, 0 ≤ m < n) -- the length of the sequence and the number of integers to delete. The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109) denoting the sequence. It is guaranteed that the sum of all n does not exceed 106.
输出描述:
For each test case, output an integer denoting the only maximum mode, or -1 if Chiaki cannot achieve it.
示例1
输入
5 5 0 2 2 3 3 4 5 1 2 2 3 3 4 5 2 2 2 3 3 4 5 3 2 2 3 3 4 5 4 2 2 3 3 4
输出
-1 3 3 3 4
题意:给你n个数,删除m个数,使得剩下的数出现次数最多的数(众数)最大。
思路:利用结构体储存每一种数个数的 个数cum 最大值val,如果要使b[i].val成为众数所需要减去的个数 sum(前缀和)
再用m跟sum比较 遍历一遍,取最大值;
代码:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int val;///数值
int num;///数量
int sum;///要使其为众数,需要减掉的数量
}b[100005];
bool cmp(node a,node b)
{
return a.num<b.num;
}
int ncase,n,k;
int a[100005];
int main()
{
while(~scanf("%d",&ncase))
{
while(ncase--){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);///对n个数进行排序
int tot=0;///一共有多少种数
int cnt=0;///数量
for(int i=1;i<n;i++){
if(a[i]!=a[i+1]){
b[++tot].val=a[i];
b[tot].num=cnt+1;
cnt=0;
}
else cnt++;
}
b[++tot].val=a[n];
b[tot].num=cnt+1;//记录val 数量
sort(b+1,b+tot+1,cmp);///按数量从小到大排序
b[1].sum=n-b[1].num-(tot-1)*(b[1].num-1);//如果要使b[1].val成为众数所需要减去的个数
int sum1=b[1].num;///储存i成为众数之前 不需要删除 的数量
cnt=2;///储存i之前不需要删除的数有几个
for(int i=2;i<=tot;i++){
if(b[i].num==b[i-1].num){///如果数量与前一个相等,那么要删除的也相等
b[i].sum=b[i-1].sum;
sum1+=b[i].num;//储存i之前不需要删除的数量
cnt++;
}
else{
sum1+=b[i].num;
b[i].sum=n-sum1-(tot-i)*(b[i].num-1);//让 b[i].val 变成众数所需减去的个数
cnt++;
}
}
int ans=-1;
for(int i=1;i<=tot;i++){///遍历找到最优解
if(b[i].sum<=k)
ans=max(b[i].val,ans);
}
printf("%d\n",ans);
}
}
return 0;
}
思路二:用map记录每种数出现的次数,用vector[i]统计存出现i次的数有哪一些,Mxa[i]记录出现i次的最大值的数,从次数 n次到1次依次用 m跟所需删去的前缀和pre 枚举每个值,符合条件就取最大值。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int n,m,k;
int a[maxn];
int Mxa[maxn];
vector<int>vc[maxn];
int ans,ct,cnt,tmp,flag;
map<int,int>mp;//mp[i][j] 数值i有j个;
void init(int n)
{
mp.clear();
for(int i=0;i<=n+5;i++)
{
vc[i].clear();
Mxa[i]=0;
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
init(n);//清空为0
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(mp.count(a[i])==0)mp[a[i]]=1;
else mp[a[i]]++;//数量++
}
for(int i=1;i<=n;i++)
{
int x=mp[a[i]];//数量
if(x) {
vc[x].push_back(a[i]);//数量 数值
Mxa[x]=max(Mxa[x],a[i]);// 该数量 数值的 最大值
mp[a[i]]=0;
}
}
int ma=-1;
int pre=0;
ans=-1;
for(int i=n;i>0&&m>=0;i--)//个数从大到小枚举
{
int xx=vc[i].size();//个数为xx
pre+=xx;//前面的个数前缀和
if(xx){//如果有
ma=max(ma,Mxa[i]);//取最大值
if(pre-1>m)//是否可以取
break;
}
ans=max(ans,ma);
m-=pre;
}
printf("%ld\n",ans);
}
return 0;
}