P2125:图书馆书架上的书题解

主要思路

这道题是数学推理的好题。

我们考虑把所有的移动全部作为单向移动,设\(x_i\)为\(i\)向\(i – 1\)移动书本的数量。显然,如果我们要把书向右移动,那么我们就把\(x_{i + 1}\)减去相应的值即可。

接下来进行推理。考虑总的答案为:\(\sum_{i = 1}^n |x_i|\)。我们要最小化这个东西。为了简化这个东西,我们可以注意到其中\(x_i\)中隐藏的递推的性质:

\[ \begin{cases} a_1 = avg – x_2 + x_1 \\ a_2 = avg – x_3 + x_2 \\ \dots \end{cases} \]

我们可以常数固定\(x_1\),进行移项和代换:

\[ x_2 = avg – a_1 + x_1 \\ x_3 = avg – a_2 + x_2 = 2avg – a_2 – a_1 + x_1 = x_1 – (a_1 + a_2 – 2avg) \\ x_n = x_1 – (\sum_{i = 1}^{n – 1} a_i – (n – 1) \times avg) \]

发现后面的求和可以递推(\(c_i = c_{i – 1} + a_i – avg\))现在变成:

\[\sum_{i = 1}^n |x_i| = |x_1 – c_1| + |x_1 – c_2| \dots \]

货仓选址,然后还原即可。

代码

// P2125.cpp
#include <bits/stdc++.h>
typedef long long ll;

using namespace std;

const int MAX_N = 5001001;

ll sum, ai[MAX_N], n, ci[MAX_N], x1, org[MAX_N];

int main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &ai[i]), sum += ai[i];
    sum /= n;
    for (int i = 1; i <= n; i++)
        ci[i] = org[i] = ci[i - 1] + ai[i] - sum;
    nth_element(ci + 1, ci + (n >> 1) + 1, ci + 1 + n), x1 = ci[(n >> 1) + 1];
    ll ans = 0;
    for (int i = 1; i <= n; i++)
        ans += llabs(ci[i] - x1);
    printf("%lld\n", ans);
    for (int i = 1; i < n; i++)
        printf("%lld %lld\n", x1 - org[i - 1], -(x1 - org[i]));
    printf("%lld %lld\n", x1 - org[n - 1], -x1);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *