糖果传递

糖果传递

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

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

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

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

输入格式

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

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

输出格式

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

数据范围

1n1000000,
0a[i]2×109,

数据保证一定有解。

输入样例:

4
1
2
5
4

输出样例:

4

 

解题思路

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

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

  因此整一个代价为|x1|+|x2|++|xn|,我们的目标就是求|x1|+|x2|++|xn|的最小值。

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

{a1x1+x2=aa2x2+x3=aan1xn1+xn=aanxn+x1=a

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

  移项,把上述方程变为

{x1x2=a1ax2x3=a2axn1xn=an1axnx1=ana

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

{x1=x10x2=x1((n1)aa2a3an)xn1=x1(2aan1an)xn=x1(aan)

这样我们就可以用x1来表示其他所有的x了。

  代入|x1|+|x2|++|xn|中,有|x10|+|x1((n1)aa2a3an)|++|x1(aan)|对于上面中的每一项,我们可以发现x1都是减去一个常数,因此可以写成|x1c1|+|x1c2|++|x1cn|

其中ci=(ni+1)a¯+j=inaj, (i2)c1=0

  我们可以惊奇的发现,要求上式的最小值,等价于要找到一个x1,使得x1分别到c1cn的距离的和最小。

  当x1取到c1cn的中位数时,上式就可以取到最小值。这个模型可以参考货仓选址这一题。这里简单证明一下。

  先假定c1c2cn

  我们把式子|x1c1|+|x2c2|++|xncn|的每一项分成若干组,第1项和第n项为一组,第2项和第n1项为一组,以此类推,如果n为奇数,那么第n2项就独自为一组,得到(|x1c1|+|x1cn|)+(|x1c2|+|x1cn1|)+由绝对值不等式||a||b|||ab||a|+|b|,得到|x1c1|+|x1cn||x1c1(x1cn)|=|cnc1|意味着当x1[c1, cn],才取到等号。对于式子的其他组,同理可得。最后式子变为(|x1c1|+|x1cn|)+(|x1c2|+|x1cn1|)+|cnc1|+|cn1c2|+我们可以发现,当每一组都取到等号,上式才可以取到最小值。因为我们一开始规定了c1c2cn,因此可以发现要给每一组都取到等号,x1的取值区间都在不断缩小,要一直到最后一组都要满足。可以发现就是取c1cn的中位数(如果n为奇数,中位数就是cn2,偶数的话,取cn2cn2+1都可以)。

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

  最后我们来看看ci是怎么求的。

  看回上面的方程组,可以发现cn=aancn1=2aan1an=cnaan1,......,c1=c2aa1=0

可以发现递推式ci=ci+1aai

  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/

posted @   onlyblues  阅读(137)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
历史上的今天:
2021-03-01 关于C++中构造函数的常见疑问
Web Analytics
点击右上角即可分享
微信分享提示