【BZOJ1045】糖果传递(基于贪心的数学题)

点此看题面

大致题意:\(n\)个小朋友坐成一圈,每人有\(a[i]\)个糖果。每人只能给左右两人传递糖果,传递一个糖果代价为1,求使所有人获得均等糖果的最小代价。

数学转换

这题其实是一道带有浓厚数学色彩的贪心题。

我们可以先用\(sum\)来统计\(a[i]\)之和,然后将\(sum\)除以\(n\),从而求出最后每个小朋友应该拥有的糖果的个数。

我们可以用\(s[i]\)来表示第\(i\)个人给第\(i+1\)个人的糖果数量(如果为负,表示第\(i+1\)个人给第\(i\)个人\(-s[i]\)颗糖果),特殊的,\(s[n]\)表示第\(n\)个人给第\(1\)个人的糖果数量。

我们可以发现,对于第\(i\)个人,他的手上拿着的糖果数应为\(a[i]+s[i-1]\),即他原本拥有的糖果和第\(i-1\)个人给他的糖果,又由于每个人应拿的糖果数为\(sum\),所以\(a[i]+s[i-1]-sum\)即为他要交给第\(i+1\)个人的糖果数。
\(s[i]=a[i]+s[i-1]-sum\)

那么\[ans=sum_{i=1}^n |s[i]-S|\]

其中,\(S\)为一个定值。而我们的目的就是取一个合适的\(S\)使\(ans\)最小。

如何求解

那么,这道题目就变成了一个我们很熟悉的一个经典数学问题了,我们可以将\(s[1]\sim s[n]\)一一对应到数轴上,不难发现,最优的\(S\)应为\(s\)数组的中位数

这样一来,这题就很简单了。

代码

#include<bits/stdc++.h>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define LL long long
#define N 1000000
using namespace std;
int n,a[N+5],s[N+5];
int read()
{
    int x=0,f=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    while(ch>='0'&&ch<='9') (x*=10)+=ch-'0',ch=getchar();
    return x*=f;
}
void write(LL x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int main()
{
    n=read();
    LL sum=0;
    for(int i=1;i<=n;i++) sum+=(a[i]=read());//用sum计算出总糖果数
    sum/=n;//将sum除以n,得出每个人应拿的糖果数
    for(int i=1;i<=n;i++) s[i]=a[i]+s[i-1]-sum;//递推求出s[]数组
    sort(s+1,s+n+1);//排序一遍,找出中位数
    LL ans=0;//ans统计答案
    for(int i=1;i<=n;i++) ans+=abs(s[i]-s[(n+1)>>1]);//求出Σ|s[i]-S|,其中S为s[]数组的中位数,即s[(n+1)>>1]
    return write(ans),0;
}

转载于:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1045.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值