uva 11300 Spreading the Wealth
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) { longlong 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