目录:算法日记
原题链接:122. 糖果传递 - AcWing题库
题目描述
有 n 个小朋友坐成一圈,每人有a[i]个糖果。
每人只能给左右两人传递糖果。
每人每次传递一个糖果代价为 1。
求使所有人获得均等糖果的最小代价。
输入格式
第一行输入一个正整数 n,表示小朋友的个数。
接下来 n 行,每行一个整数 a[i],表示第 i 个小朋友初始得到的糖果的颗数。
输出格式
输出一个整数,表示最小代价。
数据范围
1≤n≤1000000,0≤a[i]≤2×109
数据保证一定有解。
输入样例
4
1
2
5
4
输出样例
4
算法思路
为了方便描述,给定范围0≤i<n,设xi为第i个小朋友给了第i−1个小朋友xi颗糖果(即后面的小朋友给前面的小朋友糖果),ai为第i个小朋友拥有的糖果数。
送糖果给另一个小朋友时,xi为正数;从另一个小朋友收到糖果时,xi为负数。这里由于题目中说明,每人每次传递一个糖果代价为 1,因此,当xi作为糖果传递代价时始终为正数。最终的答案应为:res=∣x0∣+∣x1∣+...+∣xn−1∣,问题转换为求res最小值。
题目保证一定有解,因此每个小朋友最终拥有的糖果数:average= 糖果总数/小朋友总数。
对于第0个小朋友,他给了第n−1个小朋友x0颗糖果,第1个小朋友给了他x1颗糖果,因此,average=a0−x0+x1(①);
对于第1个小朋友,他给了第0个小朋友x1颗糖果,第2个小朋友给了他x2颗糖果,因此,average=a1−x1+x2(②);
对于第2个小朋友,他给了第1个小朋友x2颗糖果,第3个小朋友给了他x3颗糖果,因此,average=a2−x2+x3(③);
以此类推。
观察上述等式包含n个变量(x0,x1...xn−1),考虑减少变量个数。其中,由①式可消去变量x1,由②式可消去变量x2,以此类推。故可尝试进行推导:
x0=x0−0令c0=0x0=x0−c0
由①式得,
x1=average−a0+x0x1=average−a0+x0−c0令c1=a0+c0−averagex1=x0−c1
由②式得,
x2=average−a1+x1x2=average−a1+x0−c1令c2=a1+c1−averagex2=x0−c2
由③式得,
x3=average−a2+x2x3=average−a2+x0−c2令c3=a2+c2−averagex3=x0−c3
以此类推。
观察上述推导可以发现,
xi=x0−cici=ai−1+ci−1−averageres=∣x0∣+∣x1∣+...+∣xn−1∣res=∣x0−c0∣+∣x0−c1∣+...+∣x0−cn−1∣
∣x0−ci∣的几何意义为,从x0出发到ci的距离。因此,原问题进一步转化为,求从x0出发到c0,c1...cn−1的距离和的最小值。
当x0取值为数列c0,c1...cn−1中位数时,存在最小值res。
tips:注意到0≤a[i]≤2×109,因此求和存在爆int的风险,需用long long。
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6+10;
int n;
int a[N];
int c[N];
int main() {
cin>>n;
long long sum = 0;
for(int i = 0; i < n; ++i) {
cin>>a[i];
sum += a[i];
}
int ave = sum / n;
c[0] = 0;
for(int i = 1; i < n; ++i) {
c[i] = a[i-1] + c[i-1] - ave;
}
sort(c, c+n);
long long res = 0;
int mid = c[n/2];
for(int i = 0; i < n; ++i) {
res += abs(c[i] - mid);
}
cout<<res<<endl;
return 0;
}