【题目描述】
原题来自:HAOI 2008
有 n 个小朋友坐成一圈,每人有 ai 颗糖果。每人只能给左右两人传递糖果。每人每次传递一颗糖果的代价为 1 。求使所有人获得均等糖果的最小代价。
【输入格式】
第一行有一个整数 n ,表示小朋友个数;
在接下来 n 行中,每行一个整数 ai。
【输出格式】
输出使所有人获得均等糖果的最小代价。
【样例输入】
4
1
2
5
4
【样例输出】
4
【数据范围与提示】
对于 30% 的数据,n≤1000;
对于 100% 的数据,n≤10^6,保证答案可以用 64位有符号整数存储。
大致思路
原证法是方程:
-
首先,最终每个小朋友的糖果数量可以计算出来,等于糖果总数除以n,假设C1=A1减去最终每个小朋友的糖果数量,设Xi表示第i个小朋友给了第i-1个小朋友Xi颗糖果,
-
我们希望Xi的绝对值之和尽量小,即|X1| + |X1-C1| + |X1-C2| + ……+ |X1-Cn-1|要尽量小。注意到|X1-Ci|的几何意义是数轴上的点X1到Ci的距离,所以问题变成了:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数,
证明应该都懂。
我的理解是每个数减去平均数再加上上一个传递给本次的(传递可为负)。经过排序后取中位数(若为偶数取(n+1)/2),类似于最大值传递给最小值,次大值传递给次小值。最后由于到达各点距离最小点在中位数(mid)上,所以ans+=b[i]-mid;因为b[i]-mid可能为负,而传递只能是正,取绝对值abs(),即ans+=abs(b[i]-mid)
代码实现
#include<bits/stdc++.h>
using namespace std;
long long int n,a[1000055],b[1000055],c[1000055];
long long int k,m,ans=0,mid,sum=0;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
sum=sum/n;
for(long long int i=1;i<=n;i++){
b[i]=b[i-1]-a[i]+sum;
//b[i-1]-(a[i]-sum);
}
sort(b+1,b+1+n);
mid=b[(n+1)/2];
for(long long int i=1;i<=n;i++){
//cout<<b[i]<<" "<<mid<<endl;
ans+=abs(b[i]-mid);
}
cout<<ans;
return 0;
}