【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