zoukankan      html  css  js  c++  java
  • [UVA11300]Spreading the Wealth

    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 imes ave-A2-A1+X1 ]

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

    [dots ]

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

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

    [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_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;
    }
    

    幸甚至哉,歌以咏志。

  • 相关阅读:
    5 Things Every Manager Should Know about Microsoft SharePoint 关于微软SharePoint每个经理应该知道的五件事
    Microsoft SharePoint 2010, is it a true Document Management System? 微软SharePoint 2010,它是真正的文档管理系统吗?
    You think you use SharePoint but you really don't 你认为你使用了SharePoint,但是实际上不是
    Introducing Document Management in SharePoint 2010 介绍SharePoint 2010中的文档管理
    Creating Your Own Document Management System With SharePoint 使用SharePoint创建你自己的文档管理系统
    MVP模式介绍
    权重初始化的选择
    机器学习中线性模型和非线性的区别
    神经网络激励函数的作用是什么
    深度学习中,交叉熵损失函数为什么优于均方差损失函数
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/13406009.html
Copyright © 2011-2022 走看看