1. 程式人生 > >cogs 1430. [UVa 11300]分金幣

cogs 1430. [UVa 11300]分金幣

com 無符號 給定 stdin 單變量 來源 pac blog ont

1430. [UVa 11300]分金幣

★☆ 輸入文件:Wealth.in 輸出文件:Wealth.out 簡單對比
時間限制:1 s 內存限制:256 MB

【題目描述】

圓桌旁坐著n個人,每個人有一定數量的金幣技術分享,金幣數總能被n整除。每個人可以給他左右相鄰的人一些金幣,最終使得每個人的金幣數相等。你的任務是求出被轉手的金幣數量最小值。

比如,n=4,且4個人的金幣數分別是1,2,5,4時,只需轉移4枚金幣(第3個人給第2個人2枚金幣,第2個人和第4個人分別給第一個人1枚金幣)即可實現每人手中的金幣數相等。

【輸入格式】

輸入包括多組數據。每組數據的第一行為整數n(n≤1 000 000),以下n行每行為一個整數,按逆時針的順序給出每個人擁有的金幣數。輸入結束標誌為文件結束符(EOF).

【輸出格式】

對於每組數據,輸出被轉手的金幣數量的最小值。

輸入保證這個值在64位無符號整數範圍內。

【樣例輸入】

3
100
100
100
4
1
2
5
4

【樣例輸出】

0
4
【題目來源】
Spreading the Wealth ,UVa 11300

思路:  這道題目看起來很復雜,讓我們慢慢分析。首先,最終每個人的金幣數量可以計算出來,他等於金幣總數除以人數n。接下來我們用M來表示每個人最終擁有的金幣數。  假設有4個人,按順序編號為1,2,3,。假設1號給2號3枚金幣,然後2號又給了1號5枚金幣,這實際上等價於2號給1號2枚金幣,而1號什麽也沒給2號。這樣,可以設x2

表示2號給了1號多少金幣。如果x2<0,說明實際上是1給了2號-x2,枚金幣。x1,x3和x4的含義類似。註意:由於是環形,x1指的是1號給4號多少金幣。  現在假設編號為i的人初始有Ai枚金幣。對於1號來說,他給了4號x1枚金幣,還剩A1-x1枚;但因為2號給了他x2枚金幣,所以最後還剩A1-x1+x2枚金幣。根據題設,該金幣數等於M。換句話說,我們得到了一個方程:A1-x1+x2=M。  同理,對於第2個人,有A2-X2+X3=M。最終,我們可以得到n個方程,一共有n個變量,是不是可以直接解方程組了呢?很可惜,還是不行。因為從前n-1個人方程可以推導出最後一個方程。所以,實際上只有n-1個方程是有用的。
  盡管無法直接解出答案,我們還是可以嘗試著用x1表示出其他的xi,則本題就變成了單變量的極值問題。    對於第1個人,A1-x1+x2=M==>x2=M-A1+x1=x1-C1(規定C1=A1-M,下面類似)    對於第2個人,A2-x2+x3=M==>x3=M-A2+x2=2*M-A1-A2+x1=x1-C2    對於第3個人,A3-x3+x4=M==>x4=M-A3+x3=3*M-A1-A2-A3+x1=x1-C3    ······    對於第n個人,An-xn+x1=M。(這是一個多余的等式,並不能給我們更多的信息)  我們希望所有xi的絕對值之和盡可能的小,即|x1|+|x1-C1|+|x1-C2|+······+|x1-Cn-1|要最小。到|x1-Ci|的幾何意義是數軸上點x1到Ci的距離,所以問題就變成了:給定數軸上n個點,找出一個到她們的距離之和盡量小的點。  可以猜想到這個最優的x1就是這些數的“中位數”(即排序以後位於中間的數),因此只要排個序就可以了。     證明:註意,我們要證明的是:給定數軸上的n個點,在數軸上的所有點中,中位數離所有頂點的距離之和最小。凡是能轉化為這個模型的題目都可以用中位數求解並不只適用於本題。     畫出來後如下圖所示:

技術分享

     任意找一個點,比如上圖中的灰點。他的左邊有4個輸入點,右邊有2個輸入點。把他往左移動一點,不要移的太多,以免碰到輸入點。假設移動了d單位距離。則灰點左邊4個點到他的距離個減少了d,右邊兩個點到他的距離各增加了d,但總的來說,距離之和減少了2d。

     如果灰點左邊有2個點,右邊有4個點,道理類似,不過應該向右移動。換句話說,只要灰點左右的輸入點數不一樣多,就不是最優解。什麽情況下左右的輸入點一樣多呢?如果輸入點一共有奇數個,則灰點必須和中間的那個點重合(中位數);如果有偶數個,則灰點可以位於最中間的兩個點之間的任意位置(還是中位數)。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 1000100
using namespace std;
long long n,tot,ans;
long long val[MAXN],C[MAXN];
int main(){
    freopen("Wealth.in","r",stdin);
    freopen("Wealth.out","w",stdout);
    while(scanf("%d",&n)!=EOF){
        tot=0;
        for(int i=1;i<=n;i++){
            scanf("%I64d",&val[i]);
            tot+=val[i];
        }
        tot/=n;C[0]=0;
        for(int i=1;i<n;i++)
            C[i]=C[i-1]+val[i]-tot;
        sort(C,C+n);
        long long x1=C[n/2];ans=0;
        for(int i=0;i<n;i++)    ans+=abs(x1-C[i]);
        printf("%I64d\n",ans);
    }
}

cogs 1430. [UVa 11300]分金幣