2020.7.30新更新,之前不会用(LaTeX),现在加上了……
博客园上整个重写了,丢了一条LC大佬的评论QAQ
题目
解说
真是一道苏维埃气息极其浓郁的题目啊,共产主义马上就要实现了!(财富液化委员会表示很赞)
但可惜的是这道题并没有那么友善。这是一道数学题。(兄弟们把害怕打在公屏上)
在经过繁杂的思考的思考后,我觉得思路大概就是下面这样:
首先非常显然最后所有人的金币都要变成(ave=frac{sum}{n}=frac{A_1+A_2+A_3+ dots + A_n}{n})((A_i)为原数组)。
由于金币只能在相邻的人之间传递,所以我们不妨设(X_i) 代表(i) 向(i-1)号传递的硬币(正负表示方向,正数表示(i)向(i-1)传递的,负数表示(i-1)向(i)传递的,自然,(0)代表不用传递。由于是一个环,(X_1)就表示(1)和(n)号之间的关系)。
由于最后所有人金币均为(ave),所以每个人的原金币减给出去的加拿过来的结果必定是(ave),即(A_i-X_i+X_{i+1}=ave)。移个项,我们得到(X_{i+1}=ave-A_i+X_i)。由此,我们可以得到一列数组:
非常显然我们看到(X_i=(i-1) imes ave- A_{i-1} -dots-A_2-A_1+X_1),每个最后都有(X_1),但是前面不太一样,那么我们不妨把它简化一下。设(C_i=A_1+A_2+dots+A_{i-1} - (i-1) imes ave),那么我们可以化简上面这一坨式子,得到:
这样的话就可以转回来看看我们要求什么了。答案应为(vert X_1vert + vert X_2 vert + vert X_3 vert+dots+vert Xn vert)的最小值,根据上面得到的(X_i =X_1-C_i),我们的答案可化为(vert X_1 vert+vert X_1-C_2vert+vert X_1-C_2 vert +dots+vert X_1-C_nvert)。现在我们只要找一找选哪个数当做(X_1)可以使上式最小就行了。
我们知道(vert X_1-C_ivert)在数轴上表示两点之间距离,因此此题最终转化为在数轴上求一个点(X_1),使其到点(0,C_2,C_3 dots C_n)的距离之和最小。显然,(X_1)为这些数的中位数的时候这个数是最小的 (怎么证明?回去看自己的初中课本谢谢)
那么,就是这样。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1000000+2;
typedef long long ll;//数据范围显示要开long long
ll a[maxn],sum,ave,c[maxn];
int n;
int main(){
while(scanf("%d",&n)!=EOF){
sum=0;
for(int i=1;i<=n;i++) {
scanf("%lld",&a[i]);
sum+=a[i];
}
ave=sum/n;
c[1]=a[1]-ave;
for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave;
//上面这两行用于计算Ci
sort(c+1,c+1+n);
ll mid=c[n/2];//中位数
ll ans=0;
for(int i=1;i<=n;i++) ans+=abs(mid-c[i]);
printf("%lld
",ans);
}
return 0;
}
尾声
其实上面的代码是有BUG的(SURPRISE!)
由于数据比较水,上面的代码A了。但事实上你拿(5 1 2 3 4 5)试试上面的代码,答案是(4),上面的代码给的是(5) 。因为(C_{frac{n}{2}})不是中位数,(n)为偶数时有两个中位数,所以出现了问题。
完全正确的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1000000+2;
typedef long long ll;
ll a[maxn],sum,ave,c[maxn];
int n;
int main(){
while(scanf("%d",&n)!=EOF){
sum=0;
for(int i=1;i<=n;i++) {
scanf("%lld",&a[i]);
sum+=a[i];
}
ave=sum/n;
c[1]=a[1]-ave;
for(int i=2;i<=n;i++) c[i]=c[i-1]+a[i]-ave;
sort(c+1,c+1+n);
ll mid=c[n/2];
ll ans1=0;
for(int i=1;i<=n;i++) ans1+=abs(mid-c[i]);
mid=c[n/2+1];
ll ans2=0;
for(int i=1;i<=n;i++) ans2+=abs(mid-c[i]);
printf("%lld
",min(ans1,ans2));
}
return 0;
}
幸甚至哉,歌以咏志。