UVA11300 Spreading the Wealth 分金币 C++ (数学推导)

本文介绍了如何解决算法竞赛问题UVA11300,通过数学推导求解将金币均匀分配的最小调整策略。文章通过建立方程组,分析中位数的重要性,最终给出C++实现代码。

参考算法竞赛入门经典训练指南

/*
最后每个人的金币:M = (A1+A2+...+An)/n(设当前每个人的金币为Ai)
设xi表示i给i+1传递给了xi个金币(xn表示n给1传递了xn个金币)

为什么只表示第i给人给第i+1个人的金币数量?因为就算是i+1给i x个金币也可以表示i给i+1 (-x)个金币
那么每个人都满足方程组:
A1+xn-x1 = M
A2+x1-x2 = M
A3+x2-x3 = M
...
An+xn-1-xn = M

而最后答案就是求min(x1+x2+...+xn)
x1 = A1+xn-M
x2 = A2+x1-M = A2+A1+xn-M-M=A1+A2+xn-2M
x3 = A3+x2-M = A3+A2+A1+xn-M-M-M=A1+A2+A3+xn-3M
...
xn = An+xn-1-M=A1+A2+...+An+xn-nM

iM-(A1+A2+...+Ai)=Ci
那么x1+x2+...+xn =xn-C1+xn-C2+...+xn-Cn,因为无论如何传递的金币都是正数,因此可以加上绝对值
|xn-C1|+|xn-C2|+...+|xn-Cn|
那么就变成了数轴上找一个点到所有点距离最短(必须是整数)
那么那个点就是中位数,举一个简单的例子
在一个数轴上任意取6个点(不重合),假设你设定一个原点,左边有4个点,右边有2个点,那么你向左边移动d个单位(不超过左边第4个点),那么
该点到所有点的距离就会减少2d个单位(向左移d个单位左边的所有点的距离之和-4d,右边所有点的距离之和+2d),所以原点取在那里明显不行,因此
放在中位数的地方是最合适的(可以理解为最中间,即左边和右边都有3个点(可以包含),那么无论在那个区间怎么移动都是改变+3d和-3d最后结果不变)
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstring>
using namespace std;

const int maxn = 1000000 + 10;
long long A[maxn], tot, M;//A为每个人的金币,tot为总金币,M为最后每个人应该持有的金币
vector<long long>C(maxn);//C为Ci = i*M - (A1+A2+...+Ai)(上述)
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int n;
    while (cin>>n) {
        memset(A, 0, sizeof(A));
        C.resize(maxn);
        tot = 0;
        for (int i = 1; i <= n; i++) {
            cin >> A[i];
            tot += A[i];
        }
        M = tot / n;//计算M
        long long cur = 0;
        for (int i = 1; i <= n; i++) {
            cur += A[i];
            C[i] = i * M - cur;//计算Ci = i*M - (A1+A2+...+Ai)
        }
        sort(C.begin()+1,C.begin()+n+1);//排序以取中位数
        long long xn = C[(1 + n) / 2];//中位数
        long long res = 0;//结果
        for (int i = 1; i <= n; i++) {
            res += abs(xn - C[i]);//遍历每一个数轴上的点
        }
        cout << res << endl;//输出
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值