Description
圆桌旁坐着n个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数相等。你的任务是求出被转手的金币数量的最小值。比如,n=4,且4个人的金币数分别为1,2,5,4时,只需转移4枚金币(第3个人给第2个人两枚金币,第2个人和第4个人分别给第1 个人1枚金币)即可实现每人手中的金币数目相等。
输入包含多组数据。每组数据第一行为整数n(n<=1 000 000),以下n行每行为一个整数,按逆时针顺序给出每个人拥有的金币数。输入结束标志为文件结束符(EOF)。
对于每组数据,输出被转手金币数量的最小值。输入保证这个值在64位无符号整数范围内。
3
100
100
100
4
1
2
5
4
Sample Output
0
4
分析:
最终每个人的金币数量可以计算出来,它等于金币总数除以人数n。
使用M来表示每个人最终拥有的金币数。
假设有4个人,按顺序编号1,2,3,4。假设1号给2号3枚金币,然后2号又给1号5枚金币,实际上等价与2号给1号2枚金币,而1号什么也没给2号。
这样,可以设表示2号给了1号多少金币。如果<0,说明实际是1号给了2号-枚金币。,,含义类似。由于是环形,指的是1号给4号多少金币。
现在假设编号为i的人初始有枚金币。对于1号来说,他给了4号枚金币,还剩枚;但因为2号给了他枚金币,所以最后还剩下枚金币。根据题设,该金币数等于M。
=M。
同理,对于第2个人,有
最终,可以得到n个方程,一共有n个变量;但不可以直接解方程组,因为从前n-1个方程可以推导出最后一个方程。
可以尝试着用表示出其他的,此题就变成了单变量的极值问题。
对于第1个人, 下面类似。
对于第2个人,
对于第3个人,
……
对于第n个人, 这是一个多余的等式,并不能给我们太多信息。
我们希望所有的绝对值之和尽量小,即最小。
注意的几何意义是数轴上的点到的距离。所以问题变成了:给定数轴上的n个点,找出一个到它们的距离之和尽量小的点。
***最优的就是这些数的“中位数”。
证明:给定数轴上的n个点,在数轴上所有点中,中位数离所有顶点的距离之和最小。
任意找一个点,如图中灰色的点,它左边有2个输入点,右边有两个输入点。把它往左移动一点,不要移的太多,以免碰到输入点。假设移动了d单位距离,则灰色点左边4个点到它的距离各减少了d,右边点到它的距离各增加了d。总的来说 ,距离之和减少了2d。
如果灰色点的左边有2个点,右边有4个点。道理类似,不过应该向右移动。只要灰色点左右的输入点不一样多,就不是最优解。什么情况下左右的输入点一样多呢?如果输入点一共有奇数个,则灰色点必须和中间的那个点重合(中位数);如果有偶数个,则灰色点可以位于最中间的两个点之间的任意位置(还是中位数)。
Python 代码:
#初始化金币总数量
total=0
#每个人拥有的金币数存到列表A中
A=[0]
#输入人数n
n=int(input())
for i in range(n):
a=int(input())
total+=a
A.append(a)
#每人最终拥有金币数M
M=total//n
C=[0]*1000000
for i in range(1,n):
C[i]=C[i-1]+A[i]-M
#排序
C.sort()
#计算x1
x1=C[n//2]
ans=0
for i in range(n):
ans+=abs(x1-C[i])#把x1代入,计算转手的金币总数
print("转手的金币总数为:{}".format(ans))
运行结果: