Spreading the Wealth UVA - 11300 (分金币)(分析求中位数)


题目意思
圆桌旁坐着n个人,每个人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等,你的任务是求出被转手的金币数量的最小值。

这道题乍看没有什么思路,细看,也没有
这道题目看着挺复杂的,它的解析我看了三次都没有看下去,今天终于看完了;

根据题意可以这样想;假设有3个人,标号为1 2 3;

1号可以给2号或者3号金币,2号可以给3号或者1号金币,而3号可以给1号或者2号金币;
我们可以假设一个环形,
3号的金币由1号给出(可正可负可为零),2号的金币由3号给出,1号的金币由2号给出;
所以可以列出一个行列式;(M为最终平衡的状态)
A1-x1+x2=M;
A2-x2+x3=M;
A3-x3+x1=M;

我们可以理解看出就算x2大于A2,(就是指2号给1号的金币),它还可以由x3补给,A3一时拿不出x3也可以由x1加上(这里的x1,x2,x3均是可正可负可为零),
我们理想化的认为可以经过一轮交换达到平衡,虽然有可能不行,但是最终的形态就是这个样子,他需要的最终总会循环到他的手中。而且这样也包括了所有的交换,所以这样列式子是可以的,然后就是求解了;
这里我们用x1代替所有的变量
A1-x1+x2=M; ——x2=x1-A1+M;
因为A1M均为常量,所以可以用同一个值来表示
(我们可以规定C1=A1-M),所以原式就变成了
x2=x1-c1; 下面同理
A2-x2+x3=M; ——x3=x2-A2+M;——x2=x1-C2;
A3-x3+x1=M;
由于最后一条得到的为x1=所以可以不用列式子;
所以最后就演变成了求

|x1|+|x1-C1|+|x1-C2|+|x1-C3|+……|x1-Cn-1|的最小值

从几何意义上来讲|x1-Ci| 就是数轴上点x1到Ci的距离。
所以题意就又演变成了:给定的n的点,找出一个到它们距离之和尽量小的点;能够理解,中位数离他们所有的点最小。
所以就是求中位数

代码如下

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#define ll long long
using namespace std;
#define N 1000010
ll a[N],c[N];
int main()
{
    int t;
    while(~scanf("%d",&t))
    {
        ll sum=0;
        for(int i=1;i<=t;i++)
        {
            scanf("%lld",&a[i]);
            sum+=a[i];
        }
        ll tot=sum/t;
        memset(c,0,sizeof(c));
        for(int i=1;i<t;i++)
        {
            c[i]=c[i-1]+a[i]-tot;
        }
        sort(c,c+t);
        ll x1=c[t/2],ans=0;
        for(int i=0;i<t;i++)
        {
            ans+=abs(x1-c[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值