cogs 1430. [UVa 11300]分金币

1430. [UVa 11300]分金币

★☆   输入文件:Wealth.in   输出文件:Wealth.out   简单对比
时间限制:1 s   内存限制:256 MB

【题目描述】

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

比如,n=4,且4个人的金币数分别是1,2,5,4时,只需转移4枚金币(第3个人给第2个人2枚金币,第2个人和第4个人分别给第一个人1枚金币)即可实现每人手中的金币数相等。

【输入格式】

输入包括多组数据。每组数据的第一行为整数n(n≤1 000 000),以下n行每行为一个整数,按逆时针的顺序给出每个人拥有的金币数。输入结束标志为文件结束符(EOF).

【输出格式】

对于每组数据,输出被转手的金币数量的最小值。

输入保证这个值在64位无符号整数范围内。

【样例输入】

3
100
100
100
4
1
2
5
4

【样例输出】

0
4
【题目来源】
Spreading the Wealth ,UVa 11300

思路:  

  这道题目看起来很复杂,让我们慢慢分析。首先,最终每个人的金币数量可以计算出来,他等于金币总数除以人数n。接下来我们用M来表示每个人最终拥有的金币数。

  假设有4个人,按顺序编号为1,2,3,。假设1号给2号3枚金币,然后2号又给了1号5枚金币,这实际上等价于2号给1号2枚金币,而1号什么也没给2号。这样,可以设x2表示2号给了1号多少金币。如果x2<0,说明实际上是1给了2号-x2,枚金币。x1,x3和x4的含义类似。注意:由于是环形,x1指的是1号给4号多少金币。  现在假设编号为i的人初始有Ai枚金币。对于1号来说,他给了4号x1枚金币,还剩A1-x1枚;但因为2号给了他x2枚金币,所以最后还剩A1-x1+x2枚金币。根据题设,该金币数等于M。换句话说,我们得到了一个方程:A1-x1+x2=M。

  同理,对于第2个人,有A2-X2+X3=M。最终,我们可以得到n个方程,一共有n个变量,是不是可以直接解方程组了呢?很可惜,还是不行。因为从前n-1个人方程可以推导出最后一个方程。所以,实际上只有n-1个方程是有用的。

  尽管无法直接解出答案,我们还是可以尝试着用x1表示出其他的xi,则本题就变成了单变量的极值问题。

    对于第1个人,A1-x1+x2=M==>x2=M-A1+x1=x1-C1(规定C1=A1-M,下面类似)  

    对于第2个人,A2-x2+x3=M==>x3=M-A2+x2=2*M-A1-A2+x1=x1-C2    

    对于第3个人,A3-x3+x4=M==>x4=M-A3+x3=3*M-A1-A2-A3+x1=x1-C3    

    ······    

    对于第n个人,An-xn+x1=M。(这是一个多余的等式,并不能给我们更多的信息)  

  我们希望所有xi的绝对值之和尽可能的小,即|x1|+|x1-C1|+|x1-C2|+······+|x1-Cn-1|要最小。到|x1-Ci|的几何意义是数轴上点x1到Ci的距离,所以问题就变成了:给定数轴上n个点,找出一个到她们的距离之和尽量小的点。  

    可以猜想到这个最优的x1就是这些数的“中位数”(即排序以后位于中间的数),因此只要排个序就可以了。     证明:注意,我们要证明的是:给定数轴上的n个点,在数轴上的所有点中,中位数离所有顶点的距离之和最小。凡是能转化为这个模型的题目都可以用中位数求解并不只适用于本题。     画出来后如下图所示:

     任意找一个点,比如上图中的灰点。他的左边有4个输入点,右边有2个输入点。把他往左移动一点,不要移的太多,以免碰到输入点。假设移动了d单位距离。则灰点左边4个点到他的距离个减少了d,右边两个点到他的距离各增加了d,但总的来说,距离之和减少了2d。

     如果灰点左边有2个点,右边有4个点,道理类似,不过应该向右移动。换句话说,只要灰点左右的输入点数不一样多,就不是最优解。什么情况下左右的输入点一样多呢?如果输入点一共有奇数个,则灰点必须和中间的那个点重合(中位数);如果有偶数个,则灰点可以位于最中间的两个点之间的任意位置(还是中位数)。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 1000100
using namespace std;
long long n,tot,ans;
long long val[MAXN],C[MAXN];
int main(){
    freopen("Wealth.in","r",stdin);
    freopen("Wealth.out","w",stdout);
    while(scanf("%d",&n)!=EOF){
        tot=0;
        for(int i=1;i<=n;i++){
            scanf("%I64d",&val[i]);
            tot+=val[i];
        }
        tot/=n;C[0]=0;
        for(int i=1;i<n;i++)
            C[i]=C[i-1]+val[i]-tot;
        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("%I64d
",ans);
    }
}
细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
原文地址:https://www.cnblogs.com/cangT-Tlan/p/7725317.html