糖果传递

糖果传递

有 $n$ 个小朋友坐成一圈,每人有 $a \left[ i \right]$ 个糖果。

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

每人每次传递一个糖果代价为 $1$。

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

输入格式

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

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

输出格式

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

数据范围

$1 \leq n \leq 1000000$,
$0 \leq a \left[ i \right] \leq 2 \times 10^{9}$,

数据保证一定有解。

输入样例:

4
1
2
5
4

输出样例:

4

 

解题思路

  很难想到这是一道数学题。

糖果传递

  设$x_{i}$为第$i$个人给前一个人($i - 1$。其中当$i = 1$时,前一个人为第$n$个人)的糖果数量。注意,这里的$x_{i}$可以是正数或者负数。当$x_{i}$为正数,表示第$i$个人给前一个人糖果;当$x_{i}$为负数,表示前一个人给第$i$个人糖果。

  因此整一个代价为$\left| x_{1} \right| + \left| x_{2} \right| + \cdots + \left| x_{n} \right|$,我们的目标就是求$\left| x_{1} \right| + \left| x_{2} \right| + \cdots + \left| x_{n} \right|$的最小值。

  设$\overset{-}{a}$为所有糖果的平均数,最后每个人手上的糖果数量应该为$\overset{-}{a}$,列方程:

\begin{cases}
a_{1} - x_{1} + x_{2} &= \overset{-}{a} \\
a_{2} - x_{2} + x_{3} &= \overset{-}{a} \\
&\vdots \\
a_{n - 1} - x_{n - 1} + x_{n} &= \overset{-}{a} \\
a_{n} - x_{n} + x_{1} &= \overset{-}{a} \\
\end{cases}

  发现秩不为$n$,方程有无穷多解。所有式子加起来会得到常数等于常数,即有$n - 1$个独立的方程,$n$个未知数。所以存在一个*元,可以用某一个$x_{i}$来表示其他所有的$x$。

  移项,把上述方程变为

\begin{cases}
x_{1} - x_{2} &= a_{1} - \overset{-}{a} \\
x_{2} - x_{3} &= a_{2} - \overset{-}{a} \\
&\vdots \\
x_{n - 1} - x_{n} &= a_{n - 1} - \overset{-}{a} \\
x_{n} - x_{1} &= a_{n} - \overset{-}{a} \\
\end{cases}

从最后一个方程开始,每次都往前加上一个方程,得到

\begin{cases}
x_{1} &= x_{1} - 0 \\
x_{2} &= x_{1} - \left( {\left( {n - 1} \right) \cdot \overset{-}{a} - a_{2} - a_{3} - \cdots - a_{n}} \right) \\
&\vdots \\
x_{n - 1} &= x_{1} - \left( {2 \cdot \overset{-}{a} - a_{n - 1} - a_{n}} \right) \\
x_{n} &= x_{1} - \left( {\overset{-}{a} - a_{n}} \right) \\
\end{cases}

这样我们就可以用$x_{1}$来表示其他所有的$x$了。

  代入$\left| x_{1} \right| + \left| x_{2} \right| + \cdots + \left| x_{n} \right|$中,有$$\left| {x_{1} - 0} \right| + \left| {x_{1} - \left( {\left( {n - 1} \right) \cdot \overset{-}{a} - a_{2} - a_{3} - \cdots - a_{n}} \right)} \right| + \cdots + \left| {x_{1} - \left( {\overset{-}{a} - a_{n}} \right)} \right|$$对于上面中的每一项,我们可以发现$x_{1}$都是减去一个常数,因此可以写成$$\left| {x_{1} - c_{1}} \right| + \left| {x_{2} - c_{2}} \right| + \cdots + \left| {x_{n} - c_{n}} \right|$$

  我们可以惊奇的发现,要求上式的最小值,等价于要找到一个$x_{1}$,使得$x_{1}$分别到$c_{1} \sim c_{n}$的距离的和最小。

  当$x_{1}$取到$c_{1} \sim c_{n}$的中位数时,上式就可以取到最小值。这个模型可以参考货仓选址这一题。这里简单证明一下。

  先假定$c_{1} \leq c_{2} \leq \cdots \leq c_{n}$。

  我们把式子$$\left| {x_{1} - c_{1}} \right| + \left| {x_{2} - c_{2}} \right| + \cdots + \left| {x_{n} - c_{n}} \right|$$的每一项分成若干组,第$1$项和第$n$项为一组,第$2$项和第$n - 1$项为一组,以此类推,如果$n$为奇数,那么第$\left\lceil \frac{n}{2} \right\rceil$项就独自为一组,得到$$\left( {\left| {x_{1} - c_{1}} \right| + \left| {x_{1} - c_{n}} \right|} \right) + \left( {\left| {x_{1} - c_{2}} \right| + \left| {x_{1} - c_{n - 1}} \right|} \right) + \cdots$$由绝对值不等式$\left| {\left| a \right| - \left| b \right|} \right| \leq \left| {a - b} \right| \leq \left| a \right| + \left| b \right|$,得到$$\left| {x_{1} - c_{1}} \right| + \left| {x_{1} - c_{n}} \right| \geq \left| {x_{1} - c_{1} - \left( {x_{1} - c_{n}} \right)} \right| = \left| {c_{n} - c_{1}} \right|$$意味着当$x_{1} \in \left\lbrack {c_{1}, ~c_{n}} \right\rbrack$,才取到等号。对于式子的其他组,同理可得。最后式子变为$$\left( {\left| {x_{1} - c_{1}} \right| + \left| {x_{1} - c_{n}} \right|} \right) + \left( {\left| {x_{1} - c_{2}} \right| + \left| {x_{1} - c_{n - 1}} \right|} \right) + \cdots \geq \left| {c_{n} - c_{1}} \right| + \left| {c_{n - 1} - c_{2}} \right| + \cdots$$我们可以发现,当每一组都取到等号,上式才可以取到最小值。因为我们一开始规定了$c_{1} \leq c_{2} \leq \cdots \leq c_{n}$,因此可以发现要给每一组都取到等号,$x_{1}$的取值区间都在不断缩小,要一直到最后一组都要满足。可以发现就是取$c_{1} \sim c_{n}$的中位数(如果$n$为奇数,中位数就是$c_{\left\lceil \frac{n}{2} \right\rceil}$,偶数的话,取$c_{\frac{n}{2}}$或$c_{\frac{n}{2} + 1}$都可以)。

  当然,我们还可以从几何意义去考虑,可以发现只有当$x_{1}$在$c_{1} \sim c_{n}$这个区间内,$x_{1}$到$c_{1}$与$c_{n}$的距离之和才会最小,其他组也是这样分析,最后得到的结论相同。

  最后我们来看看$c_{i}$是怎么求的。

  看回上面的方程组,可以发现$c_{n} = {\overset{-}{a} - a_{n}}$,$c_{n - 1} = 2\overset{-}{a} - a_{n - 1} - a_{n} = c_{n} - \overset{-}{a} - a_{n - 1}$,......,$c_{1} = c_{2} - \overset{-}{a} - a_{1} = 0$。

可以发现递推式$c_{i} = c_{i + 1} - \overset{-}{a} - a_{i}$。

  AC代码如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int N = 1e6 + 10;
 6 
 7 int a[N];
 8 long long c[N];
 9 
10 int main() {
11     int n;
12     scanf("%d", &n);
13     
14     long long sum = 0;
15     for (int i = 1; i <= n; i++) {
16         scanf("%d", a + i);
17         sum += a[i];
18     }
19     
20     int avg = sum / n;
21     
22     // 通过递推式求ci
23     for (int i = n; i; i--) {
24         c[i] = c[i + 1] - avg + a[i];
25     }
26     
27     sort(c + 1, c + n + 1); // 排序,使得ci是递增的顺序。由于ci的位置不会影响答案,因此使得ci为递增的序列,方便求中位数
28     
29     long long ret = 0;
30     for (int i = 1; i <= n + 1 >> 1; i++) {
31         ret += c[n - i + 1] - c[i]; // 最小值就是每个点到中位数的距离的和,也就是每组区间的长度的和
32     }
33     printf("%lld", ret);
34     
35     return 0;
36 }

 

参考资料

  AcWing 122. 糖果传递(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/723/

上一篇:uniapp踩坑:轨迹回放map的moveLong的success回调在ios和安卓的执行时间不同


下一篇:均值不等式证明