圆桌旁边坐着n个人,每个人有一定数量的金币,金币的总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数量相等。您的任务是求出被转手的金币的数量的最小值。
输入包含多组数据。每组数据第一行为一个整数n(n<=1000000)0),以下n行每行为一个整数,按逆时针顺序给出每个人拥有的金币数。输入结束标志为文件结束符(EOF)
对于每组数据,输出被转手的金币的数量的最小值。输入保证这个值在 64位无符号整数的范围之内。
3
100
100
100
4
1
2
5
4
0
4
这是一道数学题
根据题意
每个人都可以给两边人传递硬币
为了简化问题
我们定向每个人i只能给下一个人x[i]枚硬币
那么我们的答案就是求\(|x_1| + |x_2| + ... + |x_n|\)的最小值
假设最终每个人分到num枚硬币
那么当前第i个人的情况是
a[i] - x[i] + x[i + 1] = num;
接下来就是推导式子
\(x_{i+1} = num - a_i + x_i\)
\(x_2 = num - a_1 + x_1\)
\(x_3 = num - a_2 + x_2\)
联立上述两个式子得到
\(x_2 = x_1 - (a_1 - num)\);
\(x_3 = x_2 - (a_1 + a_2 - 2 * num)\)
同理我们可以写出\(1\) ~ \(n\)所有的项数
所有的式子相加可以得到
$x_i = x_1 - $ \(\sum_{i = 1}^{n - 1} a_j - (n - 1) * m\)
由此可见,我们要求得答案只与\(x_1\)有关
拿一个\(tmp_i\)表示后面的一大坨\(\sum_{i = 1}^{n - 1} a_j - (n - 1) * m\)
我们的答案的表达式为
\(|x_1| + |x_2| + ... + |x_n| = |x_1| + |x_1 - tmp_2| + |x_1- tmp_3| + ... + |x_1- tmp_n|\)
根据这是一道数学题
不难想到
上述等式右边表示\(x _ 1\)到\(tmp_1\)~\(tmp_n\)的距离之和
显然,当\(x_1\)为集合$ {tmp_n} $的中位数时
该式子取得最小值
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
inline int read(){
int x = 0, w = 1;
char ch = getchar();
for(; ch > ‘9‘ || ch < ‘0‘; ch = getchar()) if(ch == ‘-‘) w = -1;
for(; ch >= ‘0‘ && ch <= ‘9‘; ch = getchar()) x = x * 10 + ch - ‘0‘;
return x * w;
}
const int maxn = 1000010;
int a[maxn], tmp[maxn];
int num, n;
signed main(){
while(scanf("%lld", &n) != EOF){
memset(a, 0, sizeof a);
memset(tmp, 0, sizeof tmp);
int ans = 0;
int sum = 0;
for(int i = 1; i <= n ;i++){
a[i] = read();
sum += a[i];
}
num = sum / n;
tmp[1] = 0;
for(int i = 1; i < n; i++){
tmp[i + 1] = num - a[i] + tmp[i];
}
sort(tmp + 1, tmp + 1 + n);
int x = tmp[n / 2];
for(int i = 1; i <= n; i++){
ans += abs(x - tmp[i]);
}
cout << ans << endl;
}
return 0;
}
UVA - 11300 Spreading the Wealth(数学题)
原文:https://www.cnblogs.com/rui-4825/p/12660086.html