UVa 11300

来源:互联网 发布:七月煤炭进出口数据 编辑:程序博客网 时间:2024/06/04 18:40

题目链接


题目描述:

圆桌旁坐着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);}}