题意
有n个小朋友坐成一圈,每人有ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。求使所有人获得均等糖果的最小代价。
题解
中位数的运用
当出现求Σ|a[i]-k|(其中k待定)的最小值,可以用货仓选址来解决。
这题比较复杂,需要推导公式后才能发现。
尽管糖果给的方向不一定,但我们可以总的看成往左给。即设p[i]表示i传出给i-1 p[i]个糖果。
所以cost=p[1]+p[2]...+p[n]
对每个人而言,有ave=a[i]+p[i+1]-p[i]
扩展开来我们可以得到n条式子,但第n条可以由前n-1条转变得到,实际有用的只有n-1条。虽然无法求解,但我们可以将其用一个未知数表示出来。
第1个小朋友,a[1]+p[2]-p[1]=ave -> p[2]=ave+p[1]-a[1] =p[1]-C[1](C[1]=a[1]-ave)
第2个小朋友,a[2]+p[3]-p[2]=ave -> p[3]=ave+p[2]-a[2] =2ave-a[1]-a[2]+p[1] =p[1]-C[2](C[2]=a[1]+a[2]-2*ave)
第3个小朋友,a[3]+p[4]-p[3]=ave -> p[4]=ave+p[3]-a[3] =3ave-a[1]-a[2]-a[3]+p[1] =p[1]-C[3](C[3]=a[1]+a[2]+a[3]-3*ave)
……
第n个小朋友,a[n]+p[1]-p[n]=ave。
将其代入cost=p[1]+p[2]...+p[n],可得cost=| p[1] |+| p[1]-C[1] |+| p[1]-C[2] |...+| p[1]-C[n] |,把p[1]看成k,求C的中位数赋给p[1]可得该式最小值。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
int n;long long sum=0;
int a[maxn],c[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
int ave=sum/n;
for(int i=1;i<=n;i++)
{
c[i]=c[i-1]+a[i]-ave;
}
sort(c+1,c+n+1);
int mid=c[(n+1)/2];//p1
long long ans=0;
for(int i=1;i<=n;i++)
{
ans+=abs(c[i]-mid);
}
printf("%lld\n",ans);
return 0;
}