一开始以为是差分,后来发现差分是在某一端一起+1或-1,所以这道题其实是贪心。
假设有n个小朋友,每个小朋友有ai个糖果,小朋友给出去的为xi,平均数为avg
每个小朋友手上剩余的:
1:a1+x2-x1=avg;//自己手上有的ai,给出去的xi,收到的x(i+1),最后结果是avg
2:a2+x3-x2=avg;
....
n-1:an-1+xn-x(n-1)=avg;
n:an+x1-xn=avg;
我们发现,如果知道x1,那么x2,x3都可以知道。而我们要求的其实是:
|x1|+|x2|+|x3|+...+|xn-1|+|xn|的最小值;
因此只要得到xn与x1的关系,这道题就转化为了一个一元函数求极值的问题。
我们假设x1已经知道,推出xn与x1的关系:
x2=x1+avg-a1;
x3=x2+avg-a2=x1+avg-a1+avg-a2;
...
xn=xn-1+avg-an-1;
由此,我们大致可以将|x1|+|x2|+|x3|+...+|xn-1|+|xn|变为一个类似:
|x1|+|x1+?|+|x1+??|...+|x1+?????|的形式。
不过加法不如减法好做,因为我们知道:
|x1|+|x1-?|+|x1-??|+...+|x1-????|的形式的集合意义其实是x1到所有点的距离之和。
因此我们可以将原式推为减法的形式:
x2=x1-(a1-avg);
x3=x2+avg-a2=x1-(a1-avg)-(a2-avg);
...
到这里可以看出规律了!
令b1=a1-avg,b2=b1+a2-avg...
bn=bn-1+an-avg;(前缀和)
则:
x2=x1-b1;
x3=x1-b2;
...
xn=x1-bn-1;
于是原式转化为:
|x1|+|x1-b1|+...+|x1-bn-1|的最小值。
画一个数轴,最小值就是当且仅当b数列排序后,
x1为b1,b2..bn的中位数,且向下取整的时候的值。
关于为什么是中位数和向下取整:
b数组:1 2 3 4 5 6
当且仅当x1取[3,4]时,|x1-b1|+...+|x1-bn-1|都为最小。
但如果x==3,那就单独+3,肯定比+4要小~
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define mem(a,x) memset(a,x,sizeof(a));
#define db double
//======================
const int N=1e6+10;
int a[N];ll b[N];
int n;
int main()
{
cin>>n;
ll sum=0,avg;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
avg=sum/n;
for(int i=2;i<=n;i++)
{
b[i]=b[i-1]+a[i]-avg;
}
sort(b+1,b+1+n);
int mid=(n+1)>>1;
int x1=b[mid];
ll ans=0;
for(int i=1;i<=n;i++)
{
ans+=abs(x1-b[i]);
}
cout<<ans;
return 0;
}