zoukankan      html  css  js  c++  java
  • [UVA11300]Spreading the Wealth

    题目

    (本来是英文题,这里直接用洛谷的翻译了,谢谢~)

    题目描述

    圆桌边坐着n个人,每个人都有一定数量的金币,总数能被n整除。每个人可以给他的左邻右舍的人一些硬币,最终使每个人的硬币数相等。您的任务是求出被转手的硬币数的最小值。

    输入格式

    输入包含多组数据。每组数据第一行为一个整数n(n<=1000000),以下n行每行1个整数,按逆时针顺序给出每个人拥有的金币数。输入结束标志为EOF。

    输出格式

    对于每组数据,输出被转手的金币数量的最小值。输入值保证这个值在64位无符号整数的范围内。

    解说

    真是一道苏维埃气息极其浓郁的题目啊,共产主义马上就要实现了!(财富液化委员会表示很赞)

    但可惜的是这道题并没有那么友善。这是一道数学题。(兄弟们把害怕打在公屏上)

    在经过繁杂的思考的思考后,我觉得思路大概就是下面这样:

    首先非常显然最后所有人的金币都要变成ave=sum/n=(A1+A2+……+An)/sum(Ai为原数组)。

    由于金币只能在相邻的人之间传递,所以我们不妨设Xi 代表 i 向 i-1号传递的硬币(正负表示方向,正数表示i 向i-1传递的,负数表示i-1向 i 传递的,自然0代表不用传递。由于是一个环,X1就表示1和n号之间的关系)。

    由于最后所有人金币均为ave,所以每个人的原金币减给出去的加拿过来的结果必定是ave,即Ai-Xi+Xi+1=ave。移个项,我们得到Xi+1=ave-Ai+Xi。由此,我们可以得到一列数组:

    X2=ave-A1+X1

    X3=ave-A2+X2=2*ave-A2-A1+X1

    X4=ave-A3+X3=3*ave-A3-A2-A1+X1

    ……

    Xn=(n-1)*ave-An-……-A2-A1+X1

    非常显然我们看到Xi=(i-1)*ave- Ai-1 -……-A2-A1+X1,每个最后都有X1,但是前面不太一样,那么我们不妨把它简化一下。设Ci=A1+A2+……+Ai - i*ave,那么我们可以化简上面这一坨式子,得到:

    X2=X1-C1

    X3=X1-C2

    X4=X1-C3

    ……  ……

    Xn=X1-C(n-1)

    这样的话就可以转回来看看我们要求什么了。答案应为|X1|+|X2|+|X3|+…+|Xn|的最小值,根据上面得到的Xi =X1-C( i -1),我们的答案可化为|X1|+|X1-C1|+|X1-C2|+…+|X1-C(n-1)|。现在我们只要找一找选哪个数当做X1可以使上式最小就行了。

    我们知道|X1-Ci|在数轴上表示两点之间距离,因此此题最终转化为在数轴上求一个点X1,使其到点0,C1,C2,……,C(n-1)的距离之和最小。显然,X1为这些数的中位数的时候这个数是最小的(怎么证明?回去看自己的初中课本谢谢)

    那么,就是这样。

    代码

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn=1000000+2;
     8 typedef long long ll;//数据范围显示要开long long
     9 ll a[maxn],sum,ave,c[maxn];
    10 int n;
    11 int main(){
    12     while(scanf("%d",&n)!=EOF){
    13         sum=0;
    14         for(int i=1;i<=n;i++) {
    15             scanf("%lld",&a[i]);
    16             sum+=a[i];
    17         }
    18         ave=sum/n;
    19         c[1]=a[1]-ave;
    20         for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave;
    21                 //上面这两行用于计算Ci
    22         sort(c+1,c+1+n);
    23         ll mid=c[n/2];//中位数
    24         ll ans=0;
    25         for(int i=1;i<=n;i++) ans+=abs(mid-c[i]);
    26         printf("%lld
    ",ans);
    27     }
    28     return 0;
    29 } 
    View Code

    尾声

    其实上面的代码是有BUG的(SURPRISE!)

    由于数据比较水,上面的代码A了。但事实上你拿5 1 2 3 4 5试试上面的代码,答案是4,上面的代码给的是5 。因为C n/2不是中位数,n为偶数时有两个中位数,所以出现了问题。

    完全正确的代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn=1000000+2;
     8 typedef long long ll;
     9 ll a[maxn],sum,ave,c[maxn];
    10 int n;
    11 int main(){
    12     while(scanf("%d",&n)!=EOF){
    13         sum=0;
    14         for(int i=1;i<=n;i++) {
    15             scanf("%lld",&a[i]);
    16             sum+=a[i];
    17         }
    18         ave=sum/n;
    19         c[1]=a[1]-ave;
    20         for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave;
    21         sort(c+1,c+1+n);
    22         ll mid=c[n/2];
    23         ll ans1=0;
    24         for(int i=1;i<=n;i++) ans1+=abs(mid-c[i]);
    25         mid=c[n/2+1];
    26         ll ans2=0;
    27         for(int i=1;i<=n;i++) ans2+=abs(mid-c[i]);
    28         printf("%lld
    ",min(ans1,ans2));
    29     }
    30     return 0;
    31 } 
    View Code

    幸甚至哉,歌以咏志。

  • 相关阅读:
    官方文档翻译-Today
    RAC & MVVM 学习资料整理
    35种常用字体
    中文字体的种类
    自言自语(三)--部分中文字体
    自言自语(二)--英文无衬线体和有衬线体
    sketch字体设置技巧(一)---通过锚点改变字体形态
    提高设计档次的8个方法
    知识汇总09~bootstrap-select在Vue中的封装
    知识汇总08~字符串截取
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12660196.html
Copyright © 2011-2022 走看看