uva 對於這個題目一開始完全沒有思路,看了題解才恍然大悟原來可以這麼巧妙的,數學真是美妙~
由於問題給出的條件可以知道每個人最終持有的金幣數量: M = sum(coin[i])/n.
對於每個人可以傳遞的方案只有兩種,1.傳給左邊的一個,i -> i-1,2.傳給右面的一個i-1 -> i
由於圍着坐可以看成一個還,設:xi表示第i個人給第i-1個人xi個金幣,如果xi是正數表示第i個人給了第i-1個人xi個金幣,如果xi十負數則可以理解成第i-1個人給了第i個人xi個金幣,而每個人本身有一定的金幣, 那麼有這麼一個等式成立: Ai-xi+x(i+1) = M
如果上面描述你感覺還是不太清楚的話可以這樣理解: 假設有4個人,
對於第1個人:A1-x1+x2 = M -> x2 = M-x1+A1 = x1-(A1-M)
對於第2個人:A2-x2+x3 = M -> x3 = M-x2+A2 = x1-(A1+A2-2M)
對於第3個人:A3-x3+x4 = M -> x4 = M-x3+A3 = x1-(A1+A2+A3-3M)
對於第四個等式就沒有利用價值了,因爲前N-1個已經可以用x1表示出來了
設Sum = |x1-(A1-M)| + |x1-(A1+A2-2M)| + ... + |x1-(A1+A2+...+An-1-(n-1)*M|;
那麼問題就可以看成求Sum的最小值,對於最小求這個最小值可以把其看成是在一個一維的數軸上x1與每個點的距離,那麼求各個點的中位數,然後算出來就是問題的答案
還有一個要注意的問題就是精度問題,題目說了要不小於2^64.
而對於求中位數這裏我使用了時間複雜度爲O(nlogn)的算法,對於題目的檜木(N <= 10^6) 滿足題目要求
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define DIR 4
#define DIM 2
#define STATUS 2
#define MAXN 1000000 + 10
#define MAXM 100000 + 10
#define oo (~0u)>>1
#define INF 0x3F3F3F3F
#define REPI(i, s, e) for(int i = s; i <= e; i ++)
#define REPD(i, e, s) for(int i = e; i >= s; i --)
static const double EPS = 1e-5;
typedef struct ArcNode_ {
int u, v, w, next;
}ArcNode;
long long ori_coin[MAXN], dis[MAXN];
int main(int argc, char const *argv[])
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif
int n, idx;
unsigned long long M, rst;
while( ~scanf("%d", &n) ) {
M = 0L;
REPI(i, 1, n) {
scanf("%lld", &ori_coin[i]);
M += ori_coin[i];
}
M /= n;
dis[0] = 0;
REPI(i, 1, n) dis[i] = dis[i-1]+ori_coin[i]-M;
sort(dis+1, dis+1+n);
idx = n>>0x1;
if( n&1 ) idx += 1;
rst = 0L;
REPI(i, 1, n) rst += abs(dis[idx]-dis[i]);
printf("%llu\n", rst);
}
return 0;
}