糖果传递(贪心)——《算法竞赛进阶指南》,微软面试题 , HAOI2008

糖果传递

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

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

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

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

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

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

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

数据范围
1≤n≤1000000,
0≤a[i]≤2×109,
数据保证一定有解。

输入样例:
4
1
2
5
4
输出样例:
4

题解

首先,最终每个小朋友的糖果数量可以计算出来,等于糖果总数除以n,用 a ‾ \overline{a} a 表示。
假设标号为i的小朋友开始有ai颗糖果,Xi表示第i个小朋友给了第i-1个小朋友Xi颗糖果,如果Xi<0,说明第i-1个小朋友给了第i个小朋友Xi颗糖果,X1表示第一个小朋友给第n个小朋友的糖果数量。 所以最后的答案就是ans=|X1| + |X2| + |X3| + ……+ |Xn|。
在这里插入图片描述

对于第一个小朋友,他给了第n个小朋友X1颗糖果,还剩a1-X1颗糖果;但因为第2个小朋友给了他X2颗糖果,所以最后还剩a1-X1+X2颗糖果。根据题意,最后的糖果数量等于 a ‾ \overline{a} a,即得到了一个方程:a1-X1+X2= a ‾ \overline{a} a

其他的方程如下列方程所示:并带入求解

{ a 1 − X 1 + X 2 = a ‾ a 2 − X 2 + X 3 = a ‾ a 3 − X 3 + X 4 = a ‾ . . . . . . a n − X n + X 1 = a ‾ \begin{cases} a_1-X_1+X_2=\overline{a}\\ a_2-X_2+X_3=\overline{a}\\ a_3-X_3+X_4=\overline{a}\\ ......\\ a_n-X_n+X_1=\overline{a}\\ \end{cases} a1X1+X2=aa2X2+X3=aa3X3+X4=a......anXn+X1=a => { X 1 = X 1 X 2 = a ‾ − a 1 + X 1 X 3 = a ‾ − a 2 + X 2 = a ‾ − a 2 + a ‾ − a 1 + X 1 X 4 = X 1 − ( ( a 1 + a 2 + a 3 ) − 3 ∗ a ‾ ) . . . . . . X n = X 1 − ( ( a 1 + a 2 + . . . + a n − 1 ) − ( n − 1 ) ∗ a ‾ ) \begin{cases} X_1=X_1\\ X_2=\overline{a}-a_1+X_1\\ X_3=\overline{a}-a_2+X_2=\overline{a}-a_2+\overline{a}-a_1+X_1\\ X_4=X_1-((a_1+a_2+a_3)-3*\overline{a})\\ ......\\ X_n=X_1-((a_1+a_2+...+a_{n-1})-(n-1)*\overline{a})\\ \end{cases} X1=X1X2=aa1+X1X3=aa2+X2=aa2+aa1+X1X4=X1((a1+a2+a3)3a)......Xn=X1((a1+a2+...+an1)(n1)a)

C n = ( a 1 + a 2 + . . . + a n − 1 ) − ( n − 1 ) ∗ a ‾ C_n=(a_1+a_2+...+a_{n-1})-(n-1)*\overline{a} Cn=(a1+a2+...+an1)(n1)a

易得: C n = C n − 1 + a n − 1 − a ‾ C_n=C_{n-1}+a_{n-1}-\overline{a} Cn=Cn1+an1a C 1 = 0 C_1=0 C1=0

因此最后的答案可以变换为 ans=|X1-C1| + |X1-C2| + |X1-C3| + ……+ |X1-Cn|。这个方程Cn是可以计算出来的

此表达式的意思为求一个点 X1 到n个点距离之和最小,求解中位数就是X1,然后计算代价就行。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6+5;
typedef long long LL;

int a[N],c[N];

int main()
{
    int n;
    scanf("%d", &n);
    LL sum = 0;  //平均数
    for (int i = 1; i <= n; i ++ ){
        scanf("%d", &a[i]);
        sum += a[i];
    }
    sum /= n;
    c[1] = 0;
    for(int i=2; i<=n; ++i){
        c[i] = c[i-1]+a[i-1]-sum;
    }
    sort(c+1,c+n+1);
    LL ans = 0; 
    
    for(int i=1; i<=n; ++i){
        ans += abs(c[i]-c[(n+1)/2]);    
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葛济维的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值