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) \times 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) \times ave\),那么我们可以化简上面这一坨式子,得到:
这样的话就可以转回来看看我们要求什么了。答案应为\(\vert X_1\vert + \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_2\vert+\vert X_1-C_2 \vert +\dots+\vert X_1-C_n\vert\)。现在我们只要找一找选哪个数当做\(X_1\)可以使上式最小就行了。
我们知道\(\vert X_1-C_i\vert\)在数轴上表示两点之间距离,因此此题最终转化为在数轴上求一个点\(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\n",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\n",min(ans1,ans2));
}
return 0;
}
幸甚至哉,歌以咏志。
来源:oschina
链接:https://my.oschina.net/u/4268886/blog/4463245