标签:sum 多少 增加 数组 blog 训练 log return 一个
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\n",ans); } return 0; }
uva 11300 Spreading the Wealth
标签:sum 多少 增加 数组 blog 训练 log return 一个
原文地址:http://www.cnblogs.com/kickit/p/7567392.html