題目:有一群人圍城一個圈,每個人有一些硬幣,他們每個人可以給兩側的人分一些硬幣,
假定硬幣可以均分,求分時需要最少的移動硬幣數量。
分析:數學題,中位數。
設最後每個人的硬幣數量是K個,即平均數;
設第i個人有C[i]个硬币,給左側、右側的人分別L[i]、R[i]個硬幣;
最後第i個人最後的硬幣數量為:R[i-1]-L[i] + C[i] - R[i] + L[i] = K;
設Z[i] = L[i] - R[i-1],即每次都看成是給左側的人硬幣(個右側則Z[i]為負數);
上式可以寫成:- Z[i] + C[i] + Z[i+1] = K;
整理得:Z[i+1] = Z[i] + K - C[i];
用Z[1]表示其他的Z[i]得到:
Z[2] = Z[1] + K - C[1];
Z[3] = Z[2] + K - C[2] = Z[1] + 2*K - C[1] - C[2];
...
為了簡化用W[1] = K - C[1];W[i] = W[i-1] + K - C[i-1],則:
Z[2] = Z[1] + W[1];
Z[3] = Z[1] + W[2];
Z[i] = Z[1] + W[i-1];
現在看看我們要求的目標函數是什麼;
我們要求傳遞的硬幣最少,其實就是SUM(|Z[i]|)最小;
利用上面推導的公式得到目標值關於Z[i]的函數關係;
f(Z[i])= | Z[1]-0 | +|Z[2] - W[2]| + ... + |Z[i] - W[i]| + ... + |Z[n] - W[n]|;
可以看出中位數是最優解。
說明:注意用long long類型。
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
long long coins[1000001];
long long w[1000001];
int main()
{
int n;
while (~scanf("%d",&n)) {
long long sum = 0LL;
for (int i = 0; i < n; ++ i) {
scanf("%lld",&coins[i]);
sum = sum + coins[i];
}
long long ave = sum/n;
w[0] = 0LL;
for (int i = 1; i < n; ++ i) {
w[i] = w[i-1] + ave - coins[i];
}
sort(w, w+n);
long long mid = w[n/2];
long long ans = 0LL;
for (int i = 0; i < n; ++ i) {
ans = ans + abs(mid-w[i]);
}
printf("%lld\n",ans);
}
return 0;
}
本文解决了一个关于一群人围成一圈分配硬币的问题,目标是通过调整硬币数量使得传递的硬币总数最少。采用数学方法,将问题转化为求解最优Z[i]值,最终证明了中位数为最优化解。

1229

被折叠的 条评论
为什么被折叠?



