有趣的思维题
[HAOI2008]糖果传递
题目描述
有 n 个小朋友坐成一圈,每人有 ai 个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1。
输入格式
小朋友个数 n,下面 n 行 ai。
对于 100% 的数据 n≤106。
输出格式
求使所有人获得均等糖果的最小代价。
题目分析
最终所有小朋友的糖果数量都是平均数,那么假设xi为i号小朋友给i−1号小朋友糖果的个数,正表示给出,负代表被给。那么对于每一个小朋友,可以列出方程:
ai−xi+xi+1=avexi+1=xi+ave−ai
带入每一个i可以得到:
⎩⎪⎪⎨⎪⎪⎧ x2=x1+ave−a1 x3=x2+ave−a2... xn=xn−1+ave−an
消元可得:
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧ x2=x1+ave−a1 x3=x1+2∗ave−a1−a2... xn=x1+(n−1)∗ave−i=1∑n−1ai
题目要求的答案为 i=1∑n∣xi∣ 那么就转化为了
i=1∑n∣x1+(i−1)∗ave−j=1∑iaj∣
那么问题就转化为了在数轴
x1
到
i∗ave−j=1∑iaj
这些点的距离之和,根据初中数学就能得知
x1为i∗ave−j=1∑iaj
的中位数时答案最小,然后就做完啦。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
#include <bits/stdc++.h>
using ll = long long;
int main() { #ifdef LOCAL freopen("in.txt", "r", stdin); #endif #ifndef LOCAL std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); #endif int n; while (std::cin >> n) { std::vector<ll> c(n); for (int i = 0; i < n; ++i) { std::cin >> c[i]; if (i) c[i] += c[i - 1]; } ll ave = c[n - 1] / n; for (int i = 0; i < n; ++i) { c[i] = (i + 1) * ave - c[i]; }
std::nth_element(c.begin(), c.begin() + n / 2, c.end()); ll x1 = c[n / 2]; ll ans = 0; for (int i = 0; i < n; ++i) { ans += std::abs(c[i] - x1); } std::cout << ans << '\n'; } return 0; }
|