AcWing 122. 糖果传递

610 阅读2分钟

目录:算法日记

原题链接:122. 糖果传递 - AcWing题库

题目描述

有 nn 个小朋友坐成一圈,每人有a[i]a[i]个糖果。

每人只能给左右两人传递糖果。

每人每次传递一个糖果代价为 11

求使所有人获得均等糖果的最小代价。

输入格式

第一行输入一个正整数 nn,表示小朋友的个数。

接下来 nn 行,每行一个整数 a[i]a[i],表示第 ii 个小朋友初始得到的糖果的颗数。

输出格式

输出一个整数,表示最小代价。

数据范围

1n10000000a[i]2×1091≤n≤1000000,0≤a[i]≤2×10^9 数据保证一定有解。

输入样例

4
1
2
5
4

输出样例

4

算法思路

为了方便描述,给定范围0i<n0≤i<n,设xix_i为第ii个小朋友给了第i1i-1个小朋友xix_i颗糖果(即后面的小朋友给前面的小朋友糖果),aia_i为第ii个小朋友拥有的糖果数。

送糖果给另一个小朋友时,xix_i为正数;从另一个小朋友收到糖果时,xix_i为负数。这里由于题目中说明,每人每次传递一个糖果代价为 11,因此,当xix_i作为糖果传递代价时始终为正数。最终的答案应为:res=x0+x1+...+xn1res=|x_0|+|x_1|+...+|x_{n-1}|,问题转换为求resres最小值。

题目保证一定有解,因此每个小朋友最终拥有的糖果数:average=average= 糖果总数/小朋友总数。

对于第00个小朋友,他给了第n1n-1个小朋友x0x_0颗糖果,第11个小朋友给了他x1x_1颗糖果,因此,average=a0x0+x1average=a_0-x_0+x_1(①);

对于第11个小朋友,他给了第00个小朋友x1x_1颗糖果,第22个小朋友给了他x2x_2颗糖果,因此,average=a1x1+x2average=a_1-x_1+x_2(②);

对于第22个小朋友,他给了第11个小朋友x2x_2颗糖果,第33个小朋友给了他x3x_3颗糖果,因此,average=a2x2+x3average=a_2-x_2+x_3(③);

以此类推。

观察上述等式包含nn个变量(x0,x1...xn1x_0,x_1...x_{n-1}),考虑减少变量个数。其中,由①式可消去变量x1x_1,由②式可消去变量x2x_2,以此类推。故可尝试进行推导:

x0=x00c0=0x0=x0c0x_0=x_0-0\\ 令c_0=0\\ x_0=x_0-c_0

由①式得,

x1=averagea0+x0x1=averagea0+x0c0c1=a0+c0averagex1=x0c1x_1=average-a_0+x_0\\ x_1=average-a_0+x_0-c_0\\ 令c_1=a_0+c_0-average\\ x_1=x_0-c_1

由②式得,

x2=averagea1+x1x2=averagea1+x0c1c2=a1+c1averagex2=x0c2x_2=average-a_1+x_1\\ x_2=average-a_1+x_0-c_1\\ 令c_2=a_1+c_1-average\\ x_2=x_0-c_2

由③式得,

x3=averagea2+x2x3=averagea2+x0c2c3=a2+c2averagex3=x0c3x_3=average-a_2+x_2\\ x_3=average-a_2+x_0-c_2\\ 令c_3=a_2+c_2-average\\ x_3=x_0-c_3

以此类推。

观察上述推导可以发现,

xi=x0cici=ai1+ci1averageres=x0+x1+...+xn1res=x0c0+x0c1+...+x0cn1x_i=x_0-c_i\\ c_i=a_{i-1}+c_{i-1}-average\\ res=|x_0|+|x_1|+...+|x_{n-1}|\\ res=|x_0-c_0|+|x_0-c_1|+...+|x_0-c_{n-1}|

x0ci|x_0-c_i|的几何意义为,从x0x_0出发到cic_i的距离。因此,原问题进一步转化为,求从x0x_0出发到c0,c1...cn1c_0,c_1...c_{n-1}的距离和的最小值。

x0x_0取值为数列c0,c1...cn1c_0,c_1...c_{n-1}中位数时,存在最小值resres

tips:注意到0a[i]2×1090≤a[i]≤2×10^9,因此求和存在爆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;
}