题目描述:
圆桌旁坐着n个人,每个人有一定数目的金币,金币总数能被n整除。每个人可以给相邻的人一些金币,最终使得每个人的金币数目相等,求被转手的金币数量的最小值。
输入第一行为整数n
接下来n行每行代表第i个人持有的金币数Ai
输出:被转手金币的最小值。
分析:首先每个人的最终状态我们是能确定的,因为最终金币相等,我们在读取时累计,读取结束后 m = sum / n 就是这个最终值。
令xi : 第i个人给了第i-1个人多少个金币(因为是一个圆桌,所以第1个人左边是第n个人,即当i = 1时 i-1 = n)
xi<0时相当于第i-1个人给第i个人金币
那么对每一个人来说: m = Ai - xi + x(i+1) 为什么不考虑i给i+1个人多少金币 因为这是可以相互转化的,比如1号给2号金币 然后2号又给1号金币 这样可以都转化为2号给1号金币
那么 x1 = Ai + x2 - m
x(i+1) = m - Ai + xi-1
即 x2 = m - A2 + x1 = x1 - C1
x3 = m - A2 + x2 = m-A3 +(m - A2 + x1) = 2*m - A3 - A2 +x1 = x1-C2
····
递推时发现每个xi都可以用一个常数+x1来代替
这样我们可以得到n个等式,但由于xn是可以由x1和xn-1得到的,所以我们实际上只得到了n-1个等式。
这样,我们换个角度,我们希望所有xi的绝对值之和尽量小,即|x1| + |x1 -C1| + |x-C2|+```|x1-Cn-1| 尽量小
把每一个C放到数轴上,我们要求的就是x1的位置,使得x1到所有C的距离之和最小
答案是: x1是所有C这个序列的中位数
实际上这是一个邮局选址问题:
明确上面几步,我们可以开始求解
第一步 求出每个人最终手中有多少金币,答案就是总金币数/n
第二步 求出每个C
第三部 利用上面的证明结论求出结果
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL A[1000000 + 5];
LL C[1000000 + 5];
int main()
{
int n;
while(~scanf("%d",&n))
{
LL m = 0;
LL s = 0;
for(int i =1;i<=n;i++)
{
scanf("%lld",&A[i]);
s += A[i];
}
m = s / n;
C[0] = 0;
for(int i = 1;i<n;i++)
{
C[i] = C[i-1] + A[i] - m; //递推求出C
}
sort(C,C+n);
LL x = C[n/2]; //x1的选址
LL ans = 0;
for(int i = 0;i<n;i++)
{
ans += abs(x - C[i]); //求出距离
}
printf("%lld\n",ans);
}
}