题目描述: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[N]; __int64 C[N]; 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; }
作者:PK0071 发表于2013-11-23 23:30:32 原文链接
阅读:79 评论:0 查看评论