• [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

    幸甚至哉,歌以咏志。

  • 相关阅读:
    Ubuntu配置sublime text 3的c编译环境
    ORA-01078错误举例:SID的大写和小写错误
    linux下多进程的文件拷贝与进程相关的一些基础知识
    ASM(四) 利用Method 组件动态注入方法逻辑
    基于Redis的三种分布式爬虫策略
    Go语言并发编程总结
    POJ2406 Power Strings 【KMP】
    nyoj 会场安排问题
    Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    Java的String、StringBuffer和StringBuilder的区别
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12660196.html
Copyright © 2020-2023  润新知