题目
(本来是英文题,这里直接用洛谷的翻译了,谢谢~)
题目描述
圆桌边坐着n个人,每个人都有一定数量的金币,总数能被n整除。每个人可以给他的左邻右舍的人一些硬币,最终使每个人的硬币数相等。您的任务是求出被转手的硬币数的最小值。
输入格式
输入包含多组数据。每组数据第一行为一个整数n(n<=1000000),以下n行每行1个整数,按逆时针顺序给出每个人拥有的金币数。输入结束标志为EOF。
输出格式
对于每组数据,输出被转手的金币数量的最小值。输入值保证这个值在64位无符号整数的范围内。
解说
真是一道苏维埃气息极其浓郁的题目啊,共产主义马上就要实现了!(财富液化委员会表示很赞)
但可惜的是这道题并没有那么友善。这是一道数学题。(兄弟们把害怕打在公屏上)
在经过繁杂的思考的思考后,我觉得思路大概就是下面这样:
首先非常显然最后所有人的金币都要变成ave=sum/n=(A1+A2+……+An)/sum(Ai为原数组)。
由于金币只能在相邻的人之间传递,所以我们不妨设Xi 代表 i 向 i-1号传递的硬币(正负表示方向,正数表示i 向i-1传递的,负数表示i-1向 i 传递的,自然0代表不用传递。由于是一个环,X1就表示1和n号之间的关系)。
由于最后所有人金币均为ave,所以每个人的原金币减给出去的加拿过来的结果必定是ave,即Ai-Xi+Xi+1=ave。移个项,我们得到Xi+1=ave-Ai+Xi。由此,我们可以得到一列数组:
X2=ave-A1+X1
X3=ave-A2+X2=2*ave-A2-A1+X1
X4=ave-A3+X3=3*ave-A3-A2-A1+X1
……
Xn=(n-1)*ave-An-……-A2-A1+X1
非常显然我们看到Xi=(i-1)*ave- Ai-1 -……-A2-A1+X1,每个最后都有X1,但是前面不太一样,那么我们不妨把它简化一下。设Ci=A1+A2+……+Ai - i*ave,那么我们可以化简上面这一坨式子,得到:
X2=X1-C1
X3=X1-C2
X4=X1-C3
…… ……
Xn=X1-C(n-1)
这样的话就可以转回来看看我们要求什么了。答案应为|X1|+|X2|+|X3|+…+|Xn|的最小值,根据上面得到的Xi =X1-C( i -1),我们的答案可化为|X1|+|X1-C1|+|X1-C2|+…+|X1-C(n-1)|。现在我们只要找一找选哪个数当做X1可以使上式最小就行了。
我们知道|X1-Ci|在数轴上表示两点之间距离,因此此题最终转化为在数轴上求一个点X1,使其到点0,C1,C2,……,C(n-1)的距离之和最小。显然,X1为这些数的中位数的时候这个数是最小的(怎么证明?回去看自己的初中课本谢谢)
那么,就是这样。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=1000000+2; 8 typedef long long ll;//数据范围显示要开long long 9 ll a[maxn],sum,ave,c[maxn]; 10 int n; 11 int main(){ 12 while(scanf("%d",&n)!=EOF){ 13 sum=0; 14 for(int i=1;i<=n;i++) { 15 scanf("%lld",&a[i]); 16 sum+=a[i]; 17 } 18 ave=sum/n; 19 c[1]=a[1]-ave; 20 for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave; 21 //上面这两行用于计算Ci 22 sort(c+1,c+1+n); 23 ll mid=c[n/2];//中位数 24 ll ans=0; 25 for(int i=1;i<=n;i++) ans+=abs(mid-c[i]); 26 printf("%lld ",ans); 27 } 28 return 0; 29 }
尾声
其实上面的代码是有BUG的(SURPRISE!)
由于数据比较水,上面的代码A了。但事实上你拿5 1 2 3 4 5试试上面的代码,答案是4,上面的代码给的是5 。因为C n/2不是中位数,n为偶数时有两个中位数,所以出现了问题。
完全正确的代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=1000000+2; 8 typedef long long ll; 9 ll a[maxn],sum,ave,c[maxn]; 10 int n; 11 int main(){ 12 while(scanf("%d",&n)!=EOF){ 13 sum=0; 14 for(int i=1;i<=n;i++) { 15 scanf("%lld",&a[i]); 16 sum+=a[i]; 17 } 18 ave=sum/n; 19 c[1]=a[1]-ave; 20 for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave; 21 sort(c+1,c+1+n); 22 ll mid=c[n/2]; 23 ll ans1=0; 24 for(int i=1;i<=n;i++) ans1+=abs(mid-c[i]); 25 mid=c[n/2+1]; 26 ll ans2=0; 27 for(int i=1;i<=n;i++) ans2+=abs(mid-c[i]); 28 printf("%lld ",min(ans1,ans2)); 29 } 30 return 0; 31 }
幸甚至哉,歌以咏志。