您的位置:首页 > 其它

Uva 11300 分金币(数学推导和中位数)

2013-11-23 23:30 423 查看
题目描述:n个人围着圆桌坐,每人都有一些硬币,现在每人都可以给左右以任意的分配方式给一定的钱,问最小转移多少硬币,使得每人的硬币数目都一样。(n<=1 000 000)

例子:

输入

4

1 2 5 4

输出

4

最好先思考下,别急着看代码,代码很短,但是别以为代码短的都是很简单的问题!

题解:

设编号i的人初始有Ai枚金币,比如1号,1号给4号x1个,2号给1号x2个,得出A1-x1+x2 = M(最终每个人需要有得金币数,平均值)

A1 - x1 + x2 = M——> x2 = M - A1 + x1 = x1 - C1 //C1 = A1 - M,下面类似

A2 - x2 + x3 = M ——> x3 = M - A2 + x2 = 2M - A1 - A2 + x1 = x1 - C2

...........................

An - xn + x1 = M

通过上面得过程转换为单变量的极值问题,上面得单变量指的是x1,通过推导得出x1与Ci的关系,但是有什么用呢?

我们要转移金币最小就是说上面设定的xi的绝对值要尽量小,再会过头看看上面式子x2 = x1 - C1,x3 = x1 - C2,xi和x1和Ci的关系就非常清楚了就是要|x1| + |x1 - C1| + |x1 - C2|,,,+ |x1 - Cn|的值尽量小,其实就是xi到Ci之间的距离,好,到底怎么才能拿到最小呢?没错就是中位数。

整理一下就是三步曲而已:1.求C数组 2.找中位数 3.算距离和

好吧一口气写了这么多,终于上代码了。。。

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

#define N 1000010
__int64 num
;
__int64 C
;

int main()
{
	int n;
	__int64 ave;
	while (cin>>n)
	{
		ave = 0;
		for (int i = 0; i < n; i++)
		{
			scanf("%I64d", &num[i]);
			ave += num[i];
		}
		ave/=n;
		//求C数组
		C[0] = 0;
		for (i = 1; i < n; i++)
			C[i] = C[i - 1] + num[i] - ave;
		
		//求中位数到C数组的距离和
		sort(C, C + n);
		__int64 ans = 0;	 //求和
		__int64 mid = C[n/2];//中位数
		for (i = 0; i<n; i++)
			ans += abs(mid - C[i]);
		
		printf("%I64d\n", ans);
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: