• “数学题”——传钱


    先看题目:

    有n个人围成一圈,每个人有一定数量的金币,每个人可以给和他相邻的人一些金币,求挪动的最少金币使他们平分金币

    这就是大意。

    是不是很简单。

    的确是没思路。我太难了。

    只能借鉴别人的方法。

    这道题其实是一道数学题。

    究竟如何做呢?

    下面是方法。

    先把我看出来的东西展示一下。

    首先,

    最终剩余的金币数是相同的,而且这个金币数我们可以直接算出来,我们设最终每个人都有的金币数为m,人数为n

    我们开一个变量ans记录n个人的金币数之和,那么m=ans/n。

    这是大部分人能想到的。

    然后重点来了:

    我们要充分发挥自己的创造力,胡编乱造开始:

    我们设b[i]为第i个人给第i-1个人的金币数。

    那么这个有啥用?

    列几个式子:

    对于第一个人,他给了第n个人b[1]个金币,同时,第二个人又给了他b[2]个金币,他的初始金币数为a[1],最终金币数为m

    所以我们可以列出式子 m=a[1]-b[1]+b[2]   -------- 1式

    同样,对于第二个人 m=a[2]-b[2]+b[3]     -------- 2式

    对于第n个人:m=a[n]-b[n]+b[1]     -------- n式

    所以m=a[n]-b[n]+b[n+1]。

    但是到了这里,我们仍然看不出来有啥用,别着急,慢慢来。

    接着胡编乱造

    为了找规律方便,我们引入一个数组c,令c[1]=a[1]-m,c[2]=a[1]+a[2]-m*2,c[n]=a[1]+a[2]+...+a[n]-m*n

    c[i]的含义并不重要,因为这是胡编乱造出来的,我也不知道,但是好用就行。

    通式为c[0]=0,c[i]=a[i]+c[i-1]-m;

    对于第二个人(由1式得):b[2]=m-a[1]+b[1]=b[1]-c[1];

    对于第三个人(由2式得):b[3]=m-a[2]+b[2]=m-a[2]+m-a[1]+b[1](将b[2]=m-a[1]+b[1]代入)=2m-a[1]-a[2]+b[1]=b[1]-c[2];

    对于第四个人(由3式得):b[4]=m-a[3]+b[3]=3m-a[1]-a[2]-a[3]+b[1]=b[1]-c[3]

    ……

    规律出来了没有?哈哈哈哈哈哈!

    我们希望所有的b[i]的绝对值之和尽量小,也就是使

    |b[1]-c[0]|+|b[1]-c[1]|+|b[1]-c[2]|+|b[1]-c[3]|+……+|b[1]-c[n-1]|最小

    对于任意两个数a、b,我们知道|a-b|的绝对值表示它们在数轴上的距离

    所以原式我们可以看成坐标为b[1]的点到以上所有点距离之和的最小值

    这时,就有一个结论,给定数轴上的n个点,在数轴上所有点中,中位数离所有顶点的距离之和最小

    所以大家是不是会了。

    记得要开long long哦

    下面是代码:

     1 #include<cmath>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<iostream>
     5 #include<cstring>
     6 using namespace std;
     7 const int maxn=1e6+5;
     8 typedef long long ll;
     9 ll a[maxn],c[maxn],n;
    10 int main(){
    11     //freopen("a.in","r",stdin);
    12     while(scanf("%d",&n)!=EOF){
    13         memset(a,0,sizeof(a));memset(c,0,sizeof(c));
    14         ll tot=0;
    15         for(ll i=1;i<=n;i++){
    16             scanf("%lld",&a[i]);
    17             tot+=a[i];
    18         }
    19         ll inin=tot/n;
    20         for(ll i=1;i<n;i++){
    21             c[i]=a[i]+c[i-1]-inin;
    22         }
    23         sort(c,c+n);
    24         ll ans=c[n/2];ll cnt=0;
    25         for(ll i=0;i<n;i++){
    26             cnt+=abs(ans-c[i]);
    27         }
    28         printf("%lld
    ",cnt);
    29     }
    30     return 0;
    31 }

    总结一下吧

    这道题与其说是数学题,不如说是看谁更能胡扯(当然要有理有据)

    完全比拼创造力和做题经验呀!

    大家可以积累一下。

  • 相关阅读:
    Java中的toString、equals方法覆写,懒汉式单例模式,及异常处理
    【Java】String类、Object类、包装类总结
    Java接口练习
    Java面向对象测试
    【Java】二叉搜索树的实现操作及应用
    【Java】实现二叉树基本操作、面试题
    SAS--宏变量
    SAS--array
    SAS--do loop until while
    SAS--设置行号、标记
  • 原文地址:https://www.cnblogs.com/DZN2004/p/12661845.html
Copyright © 2020-2023  润新知