ABC 313C Approximate Equalization 2

题意
现在给出一个数组a[n],现在你可以进行这种操作:

  • 选择i,j(1<=i,j<=n),使得a[i]=a[i]-1,a[j]=a[j]+1
    现在你可以进行无限次这种操作,现在需要你求出最少次数,使得数组中的最大值与最小值之间的差不超过1。

思路
我们考虑到每一次操作可以使得数组中的一个数加一,另一个数减一,那么无论你进行多少次操作,这个数组的和是不会变的。那么我们现在考虑到最后的情况:

  1. 数组中的所有数全部相同,并且等于数组的平均数(sum%n==0)
  2. 数组中有一些数是平均数,另一部分是平均数加1(sum%n!=0)

那么根据这个结论我们可以直接展开模拟了。我们用much1=sum%n。表示有多少项是等于sum/n+1的。然后扫一遍数组,把原本就等于sum/n+1的数字给去除,剩下的有两种情况:

  1. much1小于数组中原本等于sum/n+1的个数,那么余下的sum/n+1以及大于sum/n+1的数就全部变成sum/n,可以证明的是,将这些数全部变成sum/n的代价就是我们的答案
  2. 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;} 
posted on 2024-06-03 17:07  Linear_L  阅读(9)  评论(0编辑  收藏  举报