这道题就是一道数学推理题一个人接收左右金币可以分为俩个状态,一个收,一个接如图所示:
每个人最终的状态就是ave = sum / n个金币,就是求平均值
每个人开始的状态是有金币y[i]个所以可以求得一个方程:
y[1] - x[1] + x[2] = ave;
y[2] - x[2] + x[3] = ave;
一直到y[n - 1] - x[n - 1] + x[n] = ave;
为什么到n-1就结束因为我们要根据已知信息推出未知信息,而第n个人的所有信息由x[1]和x[n- 1]已经推出所以没有必要
x[2] = ave - y[1] + x[1] 设给出的金币为c[1] = y[1] - ave;
x[2] = x[1] - c[1];
x[3] = ave - y[2] + x[2]
x[3] = ave - y[2] + x[1] +y[1] - ave;
x[3] = 2 *ave - y[2] +y[1] + x[1];
c[2] = y[2] - ave + c[1];
x[3] = x1 - c[2],以此类推:
最后转化成|x[1]| + |x[1] - c[1]|......;
于是转移的最短金币就是求一个点到各个点的距离之和最短,所以中位数到各个点的距离之和最短.
代码如下 :
# include <iostream>
# include <numeric>
# include <algorithm>
# include <functional>
# include <list>
# include <map>
# include <set>
# include <stack>
# include <deque>
# include <queue>
# include <vector>
# include <ctime>
# include <cstdlib>
# include <cmath>
# include <string>
# include <cstring>
# include <iomanip>
using namespace std;
//推导公式就可以
const int maxn = 1000001;
long long int A[maxn];
long long int C[maxn],n;
int main(int argc, char *argv[])
{
while(scanf("%lld", &n) != EOF)
{
memset(A, 0 , sizeof(A));
memset(C, 0 , sizeof(C));
A[0] = C[0] = 0;
long long int sum = 0, m = 0;
for(int i = 1; i <= n; i++)
{
scanf("%lld", &A[i]);
sum += A[i];
}
m = sum / n;
for(int i = 1; i < n; i++)
{
C[i] = C[i - 1] + A[i] - m;
}
sort(C , C + n);
//从 0 开始
long long x1 = C[n / 2] , ans = 0;//求x1中位数
for(int i = 0; i < n; i++)
{
ans += abs(x1 - C[i]);
}
printf("%lld\n", ans);
}
return 0;
}