Description
圆桌旁坐着 n 个人, 每人有一定数量的金币, 金币总数能被 n 整除。 每个人可以给他左右相邻的人一些金币, 最终使得每个人的金币数目相等。 你的任务是求出被转手的金币数量的最小值。 比如 n=4, 且 4 个人的金币数量分别为 1,2,5,4 时, 只需转移 4 枚金币(第 3 个人给第 2 个人两枚金币, 第 2 个人和第 4 个人分别给第 1 个人 1 枚金币)即可实现每人手中的金币数目相等。
Input
输入包含多组数据。 每组数据第一行为整数 n (n <= 1 000 000), 以下 n 行每行为一个整数, 按逆时针顺序给出每个人拥有的金币数。 输入结束标志为文件结束符(EOF)。
Output
对于每组数据, 输出被转出的金币数量的最小值。 输入保证这个值在 64 位无符号整数范围内。
SampleInput
3
100
100
100
4
1
2
5
4
SampleOutput
0
4
Analyze
设现在每个人的财产为A1, A2, A3, ..., An, 最后均分的钱M = sum(Ai)/n.
Xi表示 第 i 号给第 i-1 号的金币数量,Xi < 0, 则第 i-1 号 给 第 i 号的金币。
那么:
对于第一个人: A1-X1+X2 = M => X2 = X1 - (A1 - M)
对于第二个人: A2-X2+X3 = M => X3 = X2 - A2 + M = X1 - (A1 + A2 - 2M)
对于第三个人: A3-X3+X4 = M => X4 = X3 - A3 + M = X1 - (A1 + A2 + A3 - 3M)
...
对于第 n 个人: An-Xn+X1 = M
我们要求的是所有 Xi 的绝对值之和 S 最小。即:
s = |X1| + |X2| + |X3| +... 要小。
我们可以看到 |Xi| = |X1 - 一个已知的值|, 那么它所表示的几何意义就是数轴上点 X1 到 这个已知的值Ci 的距离。
所以问题变成了: 给定数轴上的 n 个点, 请找出一个点 使得这个点到这 n 个点的距离和最小。
这个点就是这些数的中位数。
证明:
画一个数轴图,然后任意找一个点移动, 然后求距离和,可以发现:
- n 为奇数时: 中间的那个点,距离和是最短的。
- n 为偶数时, 中间的那两个点的平均值那个位置,距离和是最短的。
Code
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <fstream>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <cmath>
#include <limits.h>
using namespace std;
const int maxn = 1000001 + 10;
long long A[maxn], C[maxn];
int main()
{
int n;
while(scanf("%d", &n) == 1)
{
long long sum = 0;
for(int i=0; i<n; i++)
{
scanf("%lu", &A[i]);
sum += A[i];
}
sum = sum / n;
C[0] = 0;
for(int i=1; i<n; i++)
{
C[i] = C[i-1] + A[i] - sum;
}
sort(C, C+n);
long long X1 = C[n/2], ans=0;
for(int i=0; i<n; i++) ans += abs(X1 - C[i]);
printf("%lld\n", ans);
}
return 0;
}