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

    幸甚至哉,歌以咏志。

  • 相关阅读:
    python利用scapy模块写一个TCP路由追踪和扫描存活IP的脚本
    mitm6:通过IPv6攻破IPv4网络
    php写一个判断是否有cookie的脚本
    python写一个DDos脚本(DOS)
    分布式系统设计系列 -- 基本原理及高可用策略(转)
    在Ubuntu 16.04 上编译安装OpenCV3.2.0(Cmake + python3 + OpenCV3)(转)
    好玩的Raft动画演示,原理秒懂
    全球分布式数据库:Google Spanner(论文翻译)
    分布式系统设计系列 -- 基本原理及高可用策略 (转)
    MySQL更新优化(转)
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/13406009.html
Copyright © 2011-2022 走看看