uva 11300 Spreading the Wealth

https://vjudge.net/problem/UVA-11300

题意:

圆桌旁坐着n个人,每个人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数量相等。你的任务是求出被转手金币数量的最小值。当n = 4,4个人的数量分别为1,2,5,4,最小值是4枚金币,3给2两枚,2和4分别给1一个金币。

思路:

首先,金币的平均数可以计算出来,假设为m。

假设有4个人,编号为1,2,3,4。假设1号给2号3枚,2号给1号5枚,那么相当于2号给1号2枚,所以重复的只会增加金币的数量,无意义。所以,设x2表示2号给了1号多少个金币,那么x1表示1号给了4号多少枚金币。

这时,所有人的金币数量都可以计算出来

a1 - x1 + x2 = m

a2 - x2 + x3 = m

......

an-1 - xn-1 + xn = m

可以列出n - 1个式子,第n个式子没有意义,它可以由n - 1个式子相加得到。

之后,对每个式子做一定的变换

x2 = x1 - c1

x3 = x1 - c2

......

xn = x1 - cn-1

 

然后,根据这些式子,进一步推出ci 与 ci-1的关系是 ci = ci-1 + ai -m。

我们希望所有xi的绝对值之和最小(xi表示转手的金币数),因为每一个xi都可以用x1表示,所以,我们实际求的是

|x1| + |x1 - c1| + |x1 - c2| + ...... + |x1 - cn-1|

这时候对问题进行数学抽象,就得到了问题:

给定数轴上的n个点,找出一个点使得所有的点的距离到它的距离最小。

可以猜测这个最优的点是这些点的中位数,所以我们把c这个数组排序取中间就可以了。(具体证明见训练指南)

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;

long long c[1000005];
long long a[1000005];
int main()
{
    int n;

    while (scanf("%d",&n) != EOF)
    {
        long long sum = 0;

        for (int i = 1;i <= n;i++)
        {
            scanf("%lld",&a[i]);
            sum += a[i];
        }

        c[0] = 0;

        long long m = sum / n;

        for (int i = 1;i < n;i++) c[i] = c[i-1] + a[i] - m;

        sort(c,c+n);

        long long x = c[n / 2];

        long long ans = 0;

        for (int i = 0;i < n;i++) ans += abs(c[i]-x);

        printf("%lld\n",ans);
    }


    return 0;
}

 

posted @ 2017-09-21 10:43  qrfkickit  阅读(112)  评论(0编辑  收藏  举报