第一篇博客,从一道算法题谈起

题目描述 Description
刚进我们的新学校~首先感受到了校园的样子与小学完全不同~来到了我们的机房,我也看到了几个同学~有的认识,有的不认识!(mdzz,废话)

  现在,为了让大家更熟悉,更容易交流,于是老师决定排座位!现在,老师通过同学们的自我介绍明白了同学们互相的熟悉度,老师安排好座位后,告诉了同学们一个惊天的好消息(MHZ:哇,快说!):我们可以去参观广播操的排练现场!

   来到了现场,我们通过仔细的观察,又发现了一个秘密,我们几个过去参观的同学被老师盯上了!老师发现他们的同学做广播的动作极不整齐,让我们告诉他们应该做那个动作会用的时间最少而最整齐。

   MHZ由于是第一个到现场的,所以,这个任务就给他了~作为他的好朋友,你要帮帮他哦!

   有n个班的老师求助~我们都要帮他们(JHT:要不要给他们每人发一瓶水 MHZ:我也要,其他没意见 JHT:那还是算了~)。

   每个班里都有相对应的m个学生,由于老师初期教的不好,于是动作五花八门的!居然最多有50种动作!!!现在知道越是接近的两个动作,他们序号就越接近。而要改掉这个动作的时间就是你要改的动作的序号减去现在动作序号的绝对值~老师问你一共需要多少时间才能纠正全班的错误(LJX:提醒一下,老师只有一个,要一个一个同学地纠正~)。

输入描述 Input Description
第1行:一个整数n

第2~n+1行:首先是一个整数m,然后是m个人的动作,都用0~49的整数组成。

输出描述 Output Description
n行,每行两个整数:第一个整数表示选的动作的序号,第二个整数表示纠正动作要花的时间(LCZ:时间一样的话按序号小的输出~老师认为序号的动作越小越美观~)。

题解
相信大多数老哥看到这道题想到的就是求中位数,当然也有部分人有点懵逼,为啥呀,咋就用中位数了呢,不妨来看这张图。图中小旗标的点是我们要选择作为中转站的点,所有车辆要在小旗所在的位置汇合,要求找到总路程最短的一个点
对于为啥用中位数这事,我一开始也挺木。于是自己画了张图,一下子就想明白了(其实那时还是呆滞了许久,假装我顿悟能力比较强,跟你们说我一下子想明白了)。
显然,我们要从其他点往小旗汇合,必然要经过更接近小旗的点,而除去图中线段的总长度(这是底线,再短也不可能比总长度短),多余的路程就体现了选小旗的优劣了。
显然小旗放在最左或者最右,多余的路程是最多的,因为你前面的每一个更接近小旗的点你都要重复走一次,而放在中间,五五开嘛。假设每段长度都为1 的话,放在两端要走的路程为1+2+3+…+n=(n+1)*n/2,放在中间要走的路程为2(1+2+3+…+n/2)=(1+n/2)*n/2。放在中间自然要好很多。
说明了这题要求中位数的原因后,设计算法,最直接想到的是排序,找到中间的数。可以用内置的sort函数来。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,a[50001],b[50001];
    int work(int n) {
    int t=0,j,c[50001];
    for(j=0; j<n; j++) 
    {scanf("%d",&m);
    for(int i=0; i<m; i++)
    cin>>a[i];
    sort(a,a+m);
    if(m%2==0) {
    c[j]=a[m/2-1];
    for(int i=0; i<m; i++)
    t+=abs(c[j]-a[i]);
    b[j]=t;} 
    else {
    c[j]=a[m/2];
    for(int i=0; i<m; i++)
    t+=abs(c[j]-a[i]);
    b[j]=t;}
    t=0;}
    for(int i=0;i<n;i++)
    printf("%d %d\n",c[i],b[i]);
    }
    int main() {
    scanf("%d",&n);
    work(n);
    return 0;
    }

但但但但注意了,这题我们的动作值是0-49,很舒服,完全可以换一个思路,建立一个以动作为下标的数组,存储的值为该动作出现的数量,于是排序操作自然可以省略,代之以两个指针标记首尾,前者向后移,后者向前移,再用x,y存储首指针动作之前的人数和尾指针动作之后的人数,动作必定是个整数值。不多解释了,上代码吧。

    #include<iostream>
    #include<cmath>
    using namespace std;
    int main()
    {
            int n,m,i,j,l,r,x,y,xx,a[55],ans;
            cin>>n;
            for(i=1;i<=n;++i){
                    cin>>m;
                    int a[55]={0};
                    for(j=1;j<=m;++j){
                            cin>>xx;
                            a[xx]++;}
            x=a[0];y=a[49];l=0;r=49;
            while(l<r){
                    if(x>=y){r--;y=y+a[r];}
                    else{l++;x=x+a[l];}
            }//找中位数的操作
            ans=0;
            for( j=0;j<=49;++j)ans=ans+abs(l-j)*a[j];//就连这里的计算总和也不是一般的逐个累加,而是利用之前构造好的a数组
                cout<<l<<" "<<ans<<endl;
            }
            return 0;
    }

虽说代码量不能衡量什么,但能在平时积累更多的经验,就越是能写出精炼的代码,这其中差的不只是训练,还有思想。
多写代码,多反思,才能在神犇的路上走得更远(我想已经很少有人用犇这个字了吧hhhhh);
这篇文章还顺带了我的flag,每天写代码,5道题差不多。keep。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值