11300 Spreading the Wealth

6 篇文章 0 订阅


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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值