这道题,我是醉了,开头我用的结构体排序,输出错在案例11;
但是这道题我对map的用法有了更新的认识;
map遍历利用迭代器遍历格式:
map<int ,int >::iterator it=mm.begin();
for(;it!=mm.end();it++){
int a=it->first;
int b=it->second;
}
还有pair的相似用法
int a,b;
pair<int,int> p[20];//二维 //一维只能放一个,访问格式和初始化和而为类似(其实二维也可以用结构体OK)
for(int i=0;i<10;i++){
cin>>a>>b;
p[i]=make_pair(a,b);
}
for(int i=0;i<10;i++){
cout<<p[i].first<<" "<<p[i].second<<endl;
}
sort(p,p+10,cmp/*这里bool cmp可以自己排序*/);
题意:给你n个数的数组,然后里面不同的数字为k个,然后我需要在I(B)范围内存下这些数字;问最小需要改变个数字多少才能存下来(区间是任意取得);
读完之后就知道我可以选一个区间按照题目修改,只要到最后成立就行;
根据题意可以列出最大的k值:
但是注意这里的指数可能非常大,所以可能溢出,所以这里需要讨论,如果这个k已经很大了,说明不能可能有满足条件的,所以需要改动的个数为0;
所以接下来需要解决如何存储每个数出现的个数;1.sort。2.取mm里面的元素出来;
注意取出来之后,需要对前k个相加,之后再区间平移(+,-),因为我求的是最小的改动个数,所以需要对k固定;
所以整体思路就这样了:所需要的知识map遍历+区间平移(思维);
#include<bits/stdc++.h>
#include<map>
using namespace std;
const int maxn=4e5+10;
typedef long long ll;
ll a[maxn];
ll Cnt[maxn];//用来记录相同数的个数
map<int ,int > mm;
int main(){
ll n,I;
scanf("%lld %lld",&n,&I);
for(int i=0;i<n;i++){
scanf("%lld",a+i);
mm[a[i]]++;
}
sort(a,a+n);
ll g=0;//记录有多少个
map<int,int>::iterator it=mm.begin();
for(;it!=mm.end();it++){
Cnt[g++]=it->second;//这里的Cnt是记录每个数出现的次数(也就是相同数的个数),注意这里巧妙的把下标对应为不同种类了;如0,1,2表示有三种
}
//for(int i=0;i<g;i++)cout<<Cnt[i]<<" ";
if(((8*I/n)>18))return puts("0"),0;//这里可以用计算机算一下取值就知道了,主要是防止溢出
ll k=min(1ll<<(8*I/n),n);//因为最多就是n个不同,如果超出了n就取n,这里是二进制的位移运算,如果对二进制转十进制熟悉的就很好理解了
if(k>=g) return puts("0"),0;//如果原数组本身不同的个数(g)就小于k那么就没必要改了,所以输出0
ll ans,sum=0;
for(int i=0;i<k;i++){
sum+=Cnt[i];//计算前k个个数
}
ans=sum;
for(int i=k;i<g;i++){//总长度为g区间平移
sum+=Cnt[i];
sum-=Cnt[i-k];
ans=max(ans,sum);//每次取最大才能保证最后n-ans最小//这里是算出连续的(因为其他的可以自己选定区间变为一类)最大的不能改动的个数;
}
printf("%lld\n",n-ans);
return 0;
}