题意
有(n)个人坐成一圈,每人有(a[i])个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为(1)。求使所有人获得均等糖果的最小代价。
数据范围
(1 leq n leq 1000000)
(0 leq a[i] leq 2 imes 10^9)
思路
不妨设第(n)个人传给第(1)个人(x_1)个糖果(这里是净传递量,即第(n)个人传给第(1)个人的数量减去第(1)个人传给第(n)个人的数量),第(1)个人传给第(2)个人(x_2)个糖果,...,第(n - 1)个人传给第(n)个人(x_n)个糖果
中位数(b = frac{1}{n}(a_1 + a_2 + dots + a_n)),我们的目标是(min(|x_1| + |x_2| + dots + |x_n|))
因此我们可以根据数量关系,列出如下方程组:
[a_1 - x_1 + x_n = b\
a_2 - x_2 + x_1 = b\
dots\
a_n - x_n + x_{n - 1} = b
]
整理可得:
[x_1 = x_n - (b - a_1)\
x_2 = x_n - (2b - a_1 - a_2)\
dots\
x_{n - 1} = x_n -((n - 1)b - a_1 - a_2 - dots - a_{n - 1})\
]
因此我们的目标函数就转化为了(|x_n - (b - a_1)| + |x_n - (2b - a_1 - a_2)| + dots + |x_n - 0|)
不妨设(x = x_n);(c_1 = b - a_1, c_2 = 2b - a_1 - a_2, dots ,c_n = 0)
目标函数转化为(|x - c_1| + |x - c_2| + dots + |x - c_n|)
这样就转化成了一个特别经典的问题,就是数轴上有(n)个点,问选哪个点到这(n)个点的距离之和最小。取中位数即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1000010;
int n;
ll a[N];
ll q[N];
int main()
{
scanf("%d", &n);
ll sum = 0;
for(int i = 1; i <= n; i ++) {
scanf("%lld", &a[i]);
sum += a[i];
a[i] += a[i - 1];
}
ll b = sum / n;
for(int i = 1; i <= n; i ++) {
q[i] = i * b - a[i];
}
sort(q + 1, q + n + 1);
ll ans = 0;
for(int i = 1; i <= n / 2; i ++) ans += q[n - i + 1] - q[i];
printf("%lld
", ans);
return 0;
}