1. 程式人生 > >uva 11300 Spreading the Wealth

uva 11300 Spreading the Wealth

思路 行數 任務 cnblogs sca 問題 while 問題: color

https://vjudge.net/problem/UVA-11300

題意:

圓桌旁坐著n個人,每個人有一定數量的金幣,金幣總數能被n整除。每個人可以給他左右相鄰的人一些金幣,最終使得每個人的金幣數量相等。你的任務是求出被轉手金幣數量的最小值。當n = 4,4個人的數量分別為1,2,5,4,最小值是4枚金幣,3給2兩枚,2和4分別給1一個金幣。

思路:

首先,金幣的平均數可以計算出來,假設為m。

假設有4個人,編號為1,2,3,4。假設1號給2號3枚,2號給1號5枚,那麽相當於2號給1號2枚,所以重復的只會增加金幣的數量,無意義。所以,設x2表示2號給了1號多少個金幣,那麽x1表示1號給了4號多少枚金幣。

這時,所有人的金幣數量都可以計算出來

a1 - x1 + x2 = m

a2 - x2 + x3 = m

......

an-1 - xn-1 + xn = m

可以列出n - 1個式子,第n個式子沒有意義,它可以由n - 1個式子相加得到。

之後,對每個式子做一定的變換

x2 = x1 - c1

x3 = x1 - c2

......

xn = x1 - cn-1

然後,根據這些式子,進一步推出ci 與 ci-1的關系是 ci = ci-1 + ai -m。

我們希望所有xi的絕對值之和最小(xi表示轉手的金幣數),因為每一個xi都可以用x1表示,所以,我們實際求的是

|x1| + |x1 - c1| + |x1 - c2| + ...... + |x1 - cn-1|

這時候對問題進行數學抽象,就得到了問題:

給定數軸上的n個點,找出一個點使得所有的點的距離到它的距離最小。

可以猜測這個最優的點是這些點的中位數,所以我們把c這個數組排序取中間就可以了。(具體證明見訓練指南)

代碼:

#include <stdio.h>
#include <algorithm>
using namespace std;

long long c[1000005];
long long a[1000005];
int main()
{
    int n;

    while (scanf("%d",&n) != EOF)
    {
        long
long sum = 0; for (int i = 1;i <= n;i++) { scanf("%lld",&a[i]); sum += a[i]; } c[0] = 0; long long m = sum / n; for (int i = 1;i < n;i++) c[i] = c[i-1] + a[i] - m; sort(c,c+n); long long x = c[n / 2]; long long ans = 0; for (int i = 0;i < n;i++) ans += abs(c[i]-x); printf("%lld\n",ans); } return 0; }

uva 11300 Spreading the Wealth