大致题意: 有(n)个小朋友坐成一圈,每人有(a[i])个糖果。每人只能给左右两人传递糖果,传递一个糖果代价为1,求使所有人获得均等糖果的最小代价。
数学转换
这题其实是一道带有浓厚数学色彩的贪心题。
我们可以先用(sum)来统计(a[i])之和,然后将(sum)除以(n),从而求出最后每个小朋友应该拥有的糖果的个数。
我们可以用(s[i])来表示第(i)个人给第(i+1)个人的糖果数量(如果为负,表示第(i+1)个人给第(i)个人(-s[i])颗糖果),特殊的,(s[n])表示第(n)个人给第(1)个人的糖果数量。
我们可以发现,对于第(i)个人,他的手上拿着的糖果数应为(a[i]+s[i-1]),即他原本拥有的糖果和第(i-1)个人给他的糖果,又由于每个人应拿的糖果数为(sum),所以(a[i]+s[i-1]-sum)即为他要交给第(i+1)个人的糖果数。
即(s[i]=a[i]+s[i-1]-sum)。
那么$$ans=sum_{i=1}^n |s[i]-S|$$
其中,(S)为一个定值。而我们的目的就是取一个合适的(S)使(ans)最小。
如何求解
那么,这道题目就变成了一个我们很熟悉的一个经典数学问题了,我们可以将(s[1]sim s[n])一一对应到数轴上,不难发现,最优的(S)应为(s)数组的中位数。
这样一来,这题就很简单了。
代码
#include<bits/stdc++.h>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define LL long long
#define N 1000000
using namespace std;
int n,a[N+5],s[N+5];
int read()
{
int x=0,f=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') f=-1,ch=getchar();
while(ch>='0'&&ch<='9') (x*=10)+=ch-'0',ch=getchar();
return x*=f;
}
void write(LL x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int main()
{
n=read();
LL sum=0;
for(int i=1;i<=n;i++) sum+=(a[i]=read());//用sum计算出总糖果数
sum/=n;//将sum除以n,得出每个人应拿的糖果数
for(int i=1;i<=n;i++) s[i]=a[i]+s[i-1]-sum;//递推求出s[]数组
sort(s+1,s+n+1);//排序一遍,找出中位数
LL ans=0;//ans统计答案
for(int i=1;i<=n;i++) ans+=abs(s[i]-s[(n+1)>>1]);//求出Σ|s[i]-S|,其中S为s[]数组的中位数,即s[(n+1)>>1]
return write(ans),0;
}