zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        }
    
    
        return 0;
    }
  • 相关阅读:
    个人作业——软件工程实践总结作业
    个人作业——软件评测
    软件工程实践2019第四次作业
    软件工程实践2019第三次作业
    软件工程实践2019第二次作业
    软件工程实践2019第一次作业
    1
    个人作业——软件工程实践总结
    团队作业第二次—项目选题报告(追光的人)
    结对第二次—文献摘要热词统计及进阶需求
  • 原文地址:https://www.cnblogs.com/kickit/p/7567392.html
Copyright © 2011-2022 走看看