zoukankan      html  css  js  c++  java
  • #2019121000025-LGTD

    T112394 分金币

    题目描述

    圆桌周围坐了(n)个人,每个人有一定数量的金币,金币的总数保证是(n)的倍数,每个人可以向左或向右给出一些金币,使得最终每个人的金币数相等。你的任务是求出被转移了的金币的总数。

    比如,(n=4)时,如果四个人的金币数是(1,2,5,4),则转移4枚金币(第3个人给第2个人2枚,第2个人和第4个人分别给第1个人1枚金币)即可达到目的。

    输入格式

    包含多组数据。第一行是测试组数(T),接下来每组数据第一行为一个整数(n),以下(n)行每行为一个整数,按逆时针顺序给出每个人拥有的金币数。

    输出格式

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

    输入输出样例

    输入

    2
    3
    100
    100
    100
    4
    1
    2
    5
    4
    

    输出

    0
    4
    

    说明/提示

    (1leq T leq 10, nleq 10^6)

    标程时间复杂度:(O(Tnlogn))

    题解

    对于在座的(n)个人,记每个人现在拥有的金币数是(A_i(iin [1,n])),则金币总数(s=sum^{n}_{i=1}A_i),每个人最终得到的金币数$$y=frac{s}{n}$$
    如果1号给2号1枚,而2号给1号2枚,就相当于2号给1号1枚,所以我们用(x_2)表示2号给1号了多少金币,特别地,如果(x_2<0),实际上是1号给2号(-x_2)枚金币。注意,由于是环形,所以(x_1)表示1号给(n)号了几枚金币。

    如果有四个人,依次编号(1,2,3,4),对于1号来说,他给了4号(x_1)枚金币,但是2号又给了他(x_2)枚,我们最终得到1号最终的金币数$$y=A_1-x_1+x_2$$

    同理,对于第2个人,有(y=A_2-x_2+x_3),我们一共得到了(n)个方程,很可惜,我们不能解,因为前(n-1)个式子能推导出第(n)个,所以只有(n-1)个式子是有用的。

    尽管这样,我们还是想用(x_1)来表示其他的(x_i),有:对于第1个人,(A_1-x_1+x_2=y),于是$$x_2=y-A_1+x_1=x_1-C_1$$

    其中我们规定:$$C_i=A_i-y$$则(C_1=A_1-y)

    对于第2个人,(A_2-x_2+x_3=y),于是$$x_3=y-A_2+x_2=2y-A_1-A_2+x_1=x_1-C_2$$

    对于第2个人,(A_3-x_3+x_4=y),于是$$x_4=y-A_3+x_3=3y-A_1-A_2-A_3+x_1=x_1-C_3$$

    如此推导下去,我们可以得到若干等式。我们的目的是计算所有(x_i)的绝对值之和最小,即:$$(|x_1|+|x_1-C_1|+|x_1-C_2|+...+|x_1-C_{n-1}|)_{min}$$

    注意(|x_i-C_i|)的几何意义是数轴上点(x_i)(C_i)的距离,问题变成了:给定数轴上的(n)个点,求一个到他们的距离之和尽量小的点。

    结论是:这个点是这(n)个数的中位数。所以只需排序即可。严谨的证明如下:

    在数轴上标出这(n)个点,假定数轴上标好了六个点,从左到右编号(C_i(iin {1,2,3,4,5,6})),如果选定(C_4,C_5)之间的一点(P),之后把这个点向左(或者向右,效果相同,假定向左)(D)个单位长度,则(P')到它左边的四个点距离减少了(4D),到右边的两个点距离增加了(2D),总的来说距离减小了(2D)

    通过这样的移动,发现在奇数个数构成的数列,满足条件的数是最中间的数,而偶数个数构成的数列是最中间两个数的平均数,得证。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    using namespace std;
    const int maxn=1000000+10;
    long long A[maxn],C[maxn],tot,M;
    int T;
    int n;
    int main(){
    	//freopen("coin10.in","r",stdin); 
    	//freopen("coin10.out","w",stdout);
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d",&n);
    		tot=0;
    		memset(A,0,sizeof(A));
    		memset(C,0,sizeof(C));
    		for(int i=1;i<=n;i++){
    			scanf("%lld",&A[i]);
    			tot+=A[i];
    		}
    		M=tot/n;
    		C[0]=0;
    		for(int i=1;i<n;i++){
    			C[i]=C[i-1]+A[i]-M;
    		}
    		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("%lld
    ",ans);
    	}
    	return 0;
    } 
    
    要做就做南波万
  • 相关阅读:
    Oracle Pagination
    JAVA JSP WebContent
    Java程序员面试可能遭遇的30个技术陷阱解析
    分布式缓存系统Memcached
    java注解入门实例
    JSTL JSP Tag介绍
    注意事项
    Powershell打开、关闭VS
    IE和FF的差异(js版本)
    javascript 权威指南(09)
  • 原文地址:https://www.cnblogs.com/liuziwen0224/p/12018969.html
Copyright © 2011-2022 走看看