zoukankan      html  css  js  c++  java
  • 《训练指南》——6.15

    Uva 11300:

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

      分析:似乎有约瑟夫环的即视感,但是模拟出来的情形显然要复杂的多而且无法控制“最少”这个指标,而达到“最小”的有力工具是贪心、dp或者数学,而这里貌似与dp和贪心没有多大关联,因此我们要考虑进行数学建模。

      对于逆时针给出的n个人手中的初始金币我们记作A[i],并且对每个人从1开始进行标号。每人手里最终得到的金币是M个,我们定义i号给i+1号的金币x[i]个(注意我们赋予x[i]矢量性,即如果x[i]是负数依然有意义,表示i+1号给了i号|xi|个金币),容易得到下面的方程:

      A[i] + x[i-1] – x[i] = M.

      基于这个等式我们会列出如下的方程组:

      A[1] + x[n] - x[1] = M

      A[2] + x[1] –x[2] = M

      A[3] + x[2] –x[3] = M

      A[4] + x[3] – x[4] =M

      ……

      我们不难进行归纳,x[i] = x[i-1] - M +A[i].

      考虑到上文中我们给出的关于x[i]的矢量性定义,即我们要求min{∑|x[i]|}。我们再基于递推式,将其迭代进入所求表达式,会将其表述成如下形式:

      f(x1) = |x1-c1| + |x1 – c2| + |x1 – c3| + |x1 – c4|… =∑|x - ci|(ci是基于递推式得到的常数,ci = -A[2]-A[3]-…-A[i] + (i-1)M。求f(x)的最小值。

      观察到绝对值,我们再考虑其几何意义,即我们能将问题转化成如下的形式:

      给出数轴上n个点(c1,c2,c3,c4….),求解某个点,使得x到这n个点的距离之和最小,求得x之后带入f(x1)即可。

      那么现在我们面临的最后问题,就是x怎么求?

      对于n个点中间形成的n-1个区间段,我们设置一个动点x’任移动,能够看到如果x’所在的区间,左边的点和右边的点个数不同,则必然能够移动x’使得距离和减小,那么我们按照这个使距离和减小的方向(其实就是点较多的那个方向),继续移动,会发现当左右点的数量相同的时候(对于奇数个点就是在最中间的那个点上),距离和不再减小,如果再往这个方向移动,很容易发现距离和又会增大,由此我们不难得出结论,x取得n个数的中位数的时候,该点到其余n个点的距离和最小,即f(x)取得最小。

      基于这层数学化的分析,我们容易进行如下的编程实现。

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn = 1000005;
    int main()
    {
        int n;
        long long A[maxn];
        long long C[maxn];
        while(scanf("%d",&n) != EOF)
        {
             long long sum1 = 0 , M;
              for(int i = 1;i<= n;i++)
              {
                  scanf("%lld",&A[i]);
                  sum1 += A[i];
              }
              M = sum1 / n;
              C[1] = 0;
              long long temp = 0;
              for(int i = 2;i <= n;i++)
              {
                  temp -= A[i];
                  C[i] = temp + (i-1)*M;
              }
              sort(C+1,C+1+n);
              long long x = 0;
                if(n%2 == 1)
                   x = C[n/2+1];
                else
                   x = C[n/2];
                   //printf("%lld
    ",x);
    
    
              long long sum2 = 0;
              for(int i = 1;i <= n;i++)
                   sum2 += abs(x - C[i]);
    
              printf("%lld
    ",sum2);
        }
    }
  • 相关阅读:
    MVC设计模式
    NET Core 1.0
    《Nginx文件类型错误解析漏洞--攻击演练》 (转)
    AngularJs 基础(60分钟入门) (转)
    每个线程分配一个stack,每个进程分配一个heap;heap没有结构,因此寻址慢(转)
    声明式编程和命令式编程的比较(转)
    Android SimpleAdapter的参数
    RelativeLayout相对布局
    Tomcat、Apache、IIS这三种Web服务器来讲述3种搭建JSP运行环境的方法
    GitHub已将持续集成服务器Janky开源
  • 原文地址:https://www.cnblogs.com/rhythmic/p/5609446.html
Copyright © 2011-2022 走看看