UVa 11300 - Spreading the Wealth

来源:互联网 发布:淘宝怎么买枪 编辑:程序博客网 时间:2024/06/05 19:59

題目:有一群人圍城一個圈,每個人有一些硬幣,他們每個人可以給兩側的人分一些硬幣,

            假定硬幣可以均分,求分時需要最少的移動硬幣數量。

分析:數學題,中位數。

            設最後每個人的硬幣數量是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;}


0 0
原创粉丝点击