题目大意:一个数组含有n个元素,没次操作只能使最大的减一或者最小的加一,设最终有k个相等的元素,问最少需要操作几次。
题解:注意每次只能使最大的减一或者最大的加一,设数组元素 1,2,5 ,假设我们要将前两个变成5,该怎么操作呢?1=>2, 2=>3, 2=>3, 3=>4, 3=>4 4=>5 4=>5,也就是我们每加一次或者减一次,都会使数字中的最大值或者最小值改变。假设数组长度为n,和为sum,至少出现x个t,那么操作次数为sum-x*t,这里是将n个元素全部变为t,我们只需要x个,多操作了n-x次,所以总的操作次数为sum-x*t-(n-x)。那这个题该怎么做呢?首先排序一下,然后预处理处前缀和与后缀和,然后枚举每个数(设为x)其出现的区间为[l,r],所以我们可以将[1,l-1]内的数变成x,也可以将[r+1,n]内的数变成x,如果数量上仍不满足的话,需要将两边都变成x。最后在减去多的就行了,具体实现都在code中
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll INF=1e18+7; const ll N=2e5+7; ll arr[N]; ll dis[N],sum[N]; int main(){ ll n,m; cin>>n>>m; for(ll i=1;i<=n;i++) cin>>arr[i]; sort(arr+1,arr+1+n); for(ll i=1;i<=n;i++) dis[i]=dis[i-1]+arr[i]; for(ll i=n;i>=1;i--) sum[i]=sum[i+1]+arr[i]; ll ans=INF; ll l,r; for(ll i=1;i<=n;i=r+1){ r=l=i; while(arr[r]==arr[i]&&r<=n) r++; r--; if(r-l+1>=m){ ans=0; break; } if(r>=m){ ll num=(l-1)*arr[i]-dis[l-1]; num-=r-m; ans=min(ans,num); } if(n-l+1>=m){ ll num=sum[r+1]-(n-r)*arr[i]; num-=(n-l+1-m); ans=min(ans,num); } if(r<m&&n-l+1<m){ ll num=(l-1)*arr[i]-dis[l-1];//目前一共有r-1 num+=sum[r+1]-(n-r)*(arr[i]); num-=(n-m); ans=min(ans,num); } } cout<<ans<<endl; return 0; }