贪心+数学【p3156】 [CQOI2011]分金币 ([HAOI2008]糖果传递)

题目描述

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

分析:

设:
  每个人最后拥有的金币数为m个,
  Ai代表第i个人有的金币数量,
  Xi代表i给了上一个人多少金币.
则:
   A1-X1+X2=m;
变形——>A1-X1+X2=m  ==> X2=m+X1-A1=X1-(A1-m)
      A2-X2+X3=m  ==> X3=m-A2+X2=2m-A2+X1-A1
所以 可得 X3=X1-(A1-m)-(A2-m) 
就这样以此类推 
我们可以定义w[n]=sigama(1~n)Ai-m;//这就是前缀和啦!

因此结果就是|X1|+|X1-w1|+....+|X1-wn|

因为要求的是最小值,所以就转变为数学问题(找中点就好啦

-------------------代码--------------------

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cctype>
#define IL inline
#define RI register int
long long n,a[1000008],sum,w[1000008],ans;
//128MB= 33554432个int
// long long =2个int 
//所以 我开了  4000032个int ????? 
// 33554432
// 4000032
IL void read(long long &x){
    int f=1;x=0;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
IL void print(long long x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
IL long long abss(long long x){return  x<0  ? -x : x;}
int main()
{
    read(n);
    for(RI i=1;i<=n;i++)read(a[i]),sum+=a[i];
    sum/=n;//每个人应有的 emmmm 
    for(RI i=1;i<=n;i++)a[i]-=sum;
    for(RI i=2;i<=n;i++)w[i]=w[i-1]+a[i];
    std::sort(w+1,w+n+1);
    long long dis=w[ n%2==1 ? (n+1)>>1 : n>>1];
    for(RI i=1;i<=n;i++)
    ans+=abss(w[i]-dis);
    print(ans);
}

转载于:https://www.cnblogs.com/-guz/p/9620007.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值