ABC 313C Approximate Equalization 2
题意
现在给出一个数组a[n],现在你可以进行这种操作:
- 选择i,j(1<=i,j<=n),使得a[i]=a[i]-1,a[j]=a[j]+1
现在你可以进行无限次这种操作,现在需要你求出最少次数,使得数组中的最大值与最小值之间的差不超过1。
思路
我们考虑到每一次操作可以使得数组中的一个数加一,另一个数减一,那么无论你进行多少次操作,这个数组的和是不会变的。那么我们现在考虑到最后的情况:
- 数组中的所有数全部相同,并且等于数组的平均数(sum%n==0)
- 数组中有一些数是平均数,另一部分是平均数加1(sum%n!=0)
那么根据这个结论我们可以直接展开模拟了。我们用much1=sum%n。表示有多少项是等于sum/n+1的。然后扫一遍数组,把原本就等于sum/n+1的数字给去除,剩下的有两种情况:
- much1小于数组中原本等于sum/n+1的个数,那么余下的sum/n+1以及大于sum/n+1的数就全部变成sum/n,可以证明的是,将这些数全部变成sum/n的代价就是我们的答案
- much1大于等于数组中原本等于sum/n+1的个数,那么我们先扫一遍原数组,把大于sum/n+1的数全部变为sum/n+1,并计算代价。那么接下来又有两种情况。第一种情况是much1==0,那么剩下的大于sum/n+1的数就必须要变成sum/n,将这个代价加上前者,就是我们最后的答案。第二种情况是much1还有剩余,那么这个时候就得让小于等于sum/n的数变成sum/n+1了,我们可以先将这些数变成sum/n,然后算出代价后再加上余下的much1,这就是最后的答案。
代码
int n;
cin>>n;
int maxnum=-1e18;
int minnum=1e18;
int sum=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
maxnum=max(maxnum,a[i]);
minnum=min(minnum,a[i]);
sum+=a[i];
}
if(maxnum-minnum<=1)
{
cout<<0<<endl;
return 0;
}
int much1=sum%n;
int pj=sum/n;
//cout<<pj<<" "<<much1<<endl;
if(much1==0)
{
int much=0;
for(int i=1;i<=n;i++) much+=abs(a[i]-pj);
cout<<much/2<<endl;
}
else
{
int muchs=0;
int muchx=0;
for(int i=1;i<=n;i++)
{
if(a[i]==pj+1)
{
much1--;
if(much1==0) break;
}
}
if(much1==0)
{
for(int i=1;i<=n;i++)
{
if(a[i]>pj&&a[i]!=pj+1) muchx+=a[i]-pj;
else if(a[i]<pj) muchs+=pj-a[i];
}
cout<<muchx<<endl;
}
else
{
for(int i=1;i<=n;i++)
{
if(much1>0&&a[i]>pj+1)
{
much1--;
muchx+=a[i]-pj-1;
a[i]=pj+1;
}
if(!much1) break;
}
if(!much1)
{
for(int i=1;i<=n;i++) if(a[i]>pj+1) muchx+=a[i]-pj;
cout<<muchx<<endl;
return 0;
}
else
{
for(int i=1;i<=n;i++) if(pj>a[i]) muchs+=pj-a[i];
muchs+=much1;
cout<<muchs<<endl;
return 0;
}
}
}
return 0;}