贪心+数学【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