返回顶部

AcWing - 122 - 糖果传递 = 贪心

https://www.acwing.com/problem/content/description/124/

参考题解:https://www.acwing.com/solution/acwing/content/955/

题意:n个小朋友坐成环形,每个小朋友有a[i]颗糖,可以向左右传递,求最小的传递代价。

设x[i]为第i个小朋友给他左边的小朋友的糖果的数量,负数也可以。

那么答案就是:

\[\sum\limits_{i=1}^{n}|x[i]| \]

根据题意可以列出方程:

\[avg=a[i]-x[i]+x[i+1] \]

这n个方程互相制约,所以是关于x[1]的单变量函数。

然后假如设$$c[i]=a[i]-avg$$

就有

\[x[2]=avg-a[1]-x[1]=x[1]-c[1] \]

\[x[3]=avg-a[2]-x[2]=x[2]-c[2]=x[1]-c[1]-c[2] \]

\[x[4]=x[1]-c[1]-c[2]-c[3] \]

所以假如设$$s[n]=\sum\limits_{i=1}^{n}c[i-1]$$

就变成\(\sum\limits_{i=1}^{n}|x[i]|\)的最小值,

也就是\(\sum\limits_{i=1}^{n}|x[1]-s[i]|\),变成货仓选址问题。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n;
int a[2000005];
ll s[2000005];

ll calc() {
    ll sum = 0;
    for(int i = 1; i <= n; ++i)
        sum += a[i];
    int mean = sum / n;
    for(int i = 1; i <= n; ++i) {
        s[i] = s[i - 1] + a[i] - mean;
    }
    sort(s + 1, s + 1 + n);
    ll res = 0;
    int m = s[(n + 1) >> 1];
    for(int i = 1; i <= n; ++i)
        res += abs(s[i] - m);
    return res;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    printf("%lld\n", calc());
}
posted @ 2019-10-07 15:20  Inko  阅读(168)  评论(0编辑  收藏  举报