zoukankan      html  css  js  c++  java
  • 【贪心+中位数】【UVa 11300】 分金币

    (解方程建模+中位数求最短累积位移)


    分金币(Spreading the Wealth, UVa 11300)

      圆桌旁坐着n个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币数量的最小值。比如,n=4,且4个人的金币数量分别为1,2,5,4时,只需转移4枚金币(第3个人给第2个人两枚金币,第2个人和第4个人分别给第1个人1枚金币)即可实现每人手中的金币数目相等。

    【输入格式】

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

    【输出格式】

    对于每组数据,输出被转手金币数量的最小值。输入保证这个值在64位无符号整数范围内。

    【样例输入】

    3

    100

    100

    100

    4

    1      

    2        

    5       

    4       

    【样例输出】

    0

    4


    以后题解还是自己写吧。

    初看题目 感觉似乎有印象  当年LYP学长让我们思考过这道题。

    还记得一个重要结论 就是A给B Xb = B给A -Xb

    想沿着这个思路写贪心 结果发现失败了。。因为该贪心的地方不是这里


    可耻的看了题解委屈(不过下面的内容还是自己写的 不是复制粘贴)


    第一步:解方程建模型

    假设坐在圆桌旁的人序列为 1,2,3,4,5,6,7...n

    并且 1给2 X2个金币)

            2给3 X3个金币

    k给k+1 X k+1个金币(X 1...k+1 均可能为负数)

            n给1  X1个金币


           所以显然有以下等式 

              

              

               .......

              

           不过显然最后一个等式可以由sum 减去前几个等式得到 所以是个得不出有效信息的方程 可以舍去


                      

               x2=x1-C1;

               x3=x1-C2;

               x4=X1-C3;

               ans=|x1|+|x2|+|x3|+|x4|........+|xn|=|x1-0|+|x1-C1|+|x1-C2|.....+|x1-Cn-1|


    建模成功  

     转变求一个x1 使得ans最小

       即找一个   x1到这些点的距离之和要最小

      

       的经典模型


    不过这个经典模型我也没坐过


    所以怎么找这个x1呢


       答案就是Ci的中位数

    为什么呢?

      


        

            假设x1为最优的解

            x1左边有两个点 x2右边有4个点

    当你向右边稍微移动d 那么会发现最优解会被更新

    x1不是最优解 与假设矛盾 


    所以只要x1在左右两边点数不相等的时候不可能为最优解


    所以x1必须在左右相等的地方


    当点数为奇数时 显然这个地方有且只有中位数  

    当点数为偶数时 这个地方处于两个中位数之间均可以 不过为了计算方便 一般随便取两个中位数即可


    所以无论奇偶 选择中位数即可 所以x1=Ci的中位数


    然后计算出ans即可

    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    using namespace std;
    long long gold[1000100],M;
    int cmp(const void *i,const void *j)
    {
    	if(*(long long *)i>*(long long *)j) return 1;
    	else if(* (long long *)i==*(long long *)j) return 0;
    	else return -1;
    }
    int main()
    {
    	int n,k;
    	long long sum;
    	while(scanf("%d",&n)!=EOF)
    	{
    		sum=0;
    		for(int i=1;i<=n;i++)
    		{
    			scanf("%lld",&gold[i]);
    			sum+=gold[i];
    		}
    		M=sum/n;
     		for(int i=1;i<=n-1;i++)
    		gold[i]=gold[i-1]+M-gold[i];
    		gold[n]=0;
    		qsort(gold+1,n,sizeof(gold[1]),cmp);
    		k=(n+1)/2;
    		sum=0;
    		for(int i=1;i<=n;i++)
    		sum=sum+abs(gold[i]-gold[k]);
    		printf("%lld
    ",sum);
    	}
    	return 0;
    }


  • 相关阅读:
    C#通过文件头判断图像格式(摘录)
    devenv.exe 应用程序错误
    LINQ TO SQL中的selectMany(转)
    DragDrop registration did not succeed. (摘录)
    API各函数作用简介(转)
    Linq递归用法(摘录)
    (转)逐步为对象集合构建一个通用的按指定属性排序的方法
    何止 Linq 的 Distinct 不给力(转)
    关于sql日志文件
    DES算法的C#实现
  • 原文地址:https://www.cnblogs.com/zy691357966/p/5480468.html
Copyright © 2011-2022 走看看