题目描述: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;
}