BZOJ1045 HAOI2008 糖果传递 其他
题意:给定一个有N个点的环,每个点上有一个数字,每次可以让一个点--然后左边或右边的点++,求最少操作次数
题解:
定义第i个熊孩子向他左边(也就是第i-1个熊孩子)传递的糖果数为Ci,根据题意最后每个熊孩子手里都有A=sum(ai)/n个糖果,因此:a1-C1+C2=a2-C2+C3=……=an-Cn+a1=A
因此C2=A+C1-a1,C3=A+C2-a2,C4=A+C2-a2……Cn=A+Cn-1-an-1。(C可以是负数,这样就是i-1向i传递糖果)
A固定,ai固定,因此让sum(Ci)尽可能的小,定义Di=ai-Ci,那么Ci=A-Di-1。由于A固定,并且Ci都可以表示为C1-Di-1,因此就是找到一个数M,使得sum(|Ci-M|)最小。
可以将C1到Cn按顺序放到数轴上,那么就是找到一个点,使得这个点到其他点的距离和最小。假定C已经有序,那么到C1和Cn距离最小的点是两者之间的任一点,到C2和Cn-1距离最小的点是两者间任一点……一层一层的往里走,就会发现到每个点距离和最小的点就是这些数的中位数。因此M=Cn/2+1。(如果是偶数,那么点一定落在Cn/2和Cn/2+1上,由于无论在这两个点的哪个点,距离和都相同)
#include <cstdio> #include <iostream> #include <algorithm> #include <cstdlib> #include <cstring> using namespace std; int n,a[1000001],c[1000001],ave; long long sum,ans; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sum+=a[i]; } ave=sum/n; for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave; sort(c+1,c+n+1); int mid=c[(n>>1)+1]; for(int i=1;i<=n;i++) ans+=abs(c[i]-mid); printf("%lld",ans); return 0; }