[UVA11300]Spreading the Wealth

末鹿安然 提交于 2020-08-08 12:51:12

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_2=ave-A_1+X_1 \]

\[X_3=ave-A_2+X_2=2 \times ave-A2-A1+X1 \]

\[X4=ave-A3+X3=3 \times ave-A3-A2-A1+X1 \]

\[\dots \]

\[X_n=(n-1) \times ave-A_{n-1}-\dots-A_2-A_1+X_1 \]

非常显然我们看到\(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\),那么我们可以化简上面这一坨式子,得到:

\[C_1=0 \]

\[X_2=X_1-C_2 \]

\[X_3=X_1-C_3 \]

\[X_4=X_1-C_4 \]

\[\dots \]

\[X_n=X_1-C_n \]

这样的话就可以转回来看看我们要求什么了。答案应为\(\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;
}

幸甚至哉,歌以咏志。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!