专题首页 奥赛简介 通知公告 奥赛动态 历年试题 教学资源 奥赛论坛 在线考试 交流空间
  • 培训资料
  • 教学课件
  • 算法学习
  • 软件下载
  • 每周一题
  • 相关教程
  • 名师推荐
  • 奥赛政策
  • · 信息学奥林匹克
    · 信息学ACM社区
    · 信息学奥林匹克论坛
    · 信息学初学者之家
    · 中国教育曙光网信息学奥赛
    · 大榕树编程世界
    · 汕头信息学竞赛
    · 青少年信息学奥林匹克竞赛
    北京青少年信息学奥林匹克活动
    2010年NOIP 普及组解题报告

    2010-11-28 阅读次数:

     

    2010NOIP 普及组解题报告

    北京市八一中学 王祺磊

    感谢:

    我也学习一下,现在的流行趋势,先开始感谢,但这真的是发自内心的感觉,让我发出一下这些感谢。

    一、感谢和我一起并肩战斗信息学奥赛教学一线的老师们,是你们给了我温暖,让我感受到一个温馨的集体,在其中,我们一起学习,一起研讨;

    二、感谢一直和我一起战斗在一起的孩子们,是你们跟我一起,在风雨中奋斗,一起成长;

    三、感谢我的几位师长,是你们一直在我成长的过程中,指引我前进的方向;

    四、特别感谢,几位与我一起成长起来的“小牛”,是你们,反馈给了我很多需要学习的知识,让我逐渐成熟。

     

    关于2010年普及组考题:

    没有想到,这次的题目和20082009年的题目完全风格两样,我不知道是不是出题老师发生了变化。但是,总体感觉,题目趋于容易,甚至没有涉及DP、搜索中的初级方法。但是,题目很多知识点又隐含颇多。不容易拿到完美的分数。感谢出题老师,给我们带来的这届优秀题目。

     

    题目分析:

    一、数字统计

    此题,曾经在某OJ上看到过原题。要求和实现都非常简单。无非是,枚举出所有在范围内的数字,然后对数字进行拆分,对每一位数字进行判断。

    一个朴素的for循环,嵌套一个while循环,就可以解决这道题目。

    下面是程序的核心部分:

    for ( i = l ; i <= r ; i++ )

    {

    t = i;

        while (t > 0)

        {

              y = t % 10;

              if (y == 2) s++;

              t /= 10;

        }

    }

    此题丢分,绝对可以认为是绝不应该出现的事情。往参赛选手能够更加注意自己的程序细节,避免问题出现。

     

    二、接水问题

    问题描述隐晦,引导学生朝着纯枚举的思想前进,部分用秒枚举的学生会导致严重超时。但题目的核心思想却应该是模拟问题发生的本质顺序。也就是,每个新加入的人去接水的位置,一定是当前数列中和最小的那列。所以实现的方法,就是每个新数字,进入前,找出当前序列中最小的位置,加入进去。直到所有的数字都加入进去后结束。最终从所有的数字中,找出最大的那个值,即为所求。

    但是,其实有更合适的模型——插入法排序。

    即从大到小排序后,无非就是把数字加入到最后一个数值之上,然后运用插入法排序原理,把这个新数插入到合适位置。最终输出的是数组的最大值即可。

    下面是程序的核心部分:

    首先,对前m个数进行排序,然后后面的n m个数,就需要模拟插入了:

        for (i = m ; i < n ; i++ )

        {

          cin >> t;

          t += a[m - 1];

          j = m - 1;

          while (j > 0 && a[j - 1] < t)

          {

            a[j] = a[j - 1];

            j--;

          }

          a[j] = t;

        }

    最后a[0]一定是最大值。

    非常想用这道题,告诉那些忽视插入法排序的学生,当你写不好快排的时候,插入法,帮我们解决了很多为了一个数值而要排全部数值的问题。虽然效率和选择、冒泡一样,但插入法确实有它特殊之处。

     

    三、导弹拦截

    再次见到导弹拦截,颇感亲切,但是这次的导弹拦截,加入了立体环节,也从借用原题概念,让一个不存在的动态规划方法,影响解题思路。

    其实,题目的核心解题思想还是贪心和枚举。只不过要枚举的有些策略而已。我也曾试过了,用单位长度去枚举两个点的半径,结果残酷的只过了两个点。还是经过学生的解释,明白了排序后的贪心策略。所以,向学生学习,也是老师的必修课程。我再次强调,我经常向学生学习,教学相长,不外乎如此。所以,希望更多的老师,能够时常放下自己的架子和身份,多多向学生请教,我们共同的成长。

    按照到第一个点的距离平方排序之后,就可以不断让最远的点,不用离开第一点半径,进入第二点半径。在这个过程中,第一点半径逐渐缩小,第二点半径,可能发生增大。就需要一步步统计出,两个半径平方的最小值。最终达到题目要求。

    因为考虑到点的数量是100000,所以,无比需要使用快速排序。题目如果想写的较为简洁,还是使用结构体比较方便。

    首先定义一个结构体,包括x,y,jr1(距离第1点半径平方),jr2(距离第2点半径平方)。

    struct dian

    {

      int x;

      int y;

      int j1r;

      int j2r;

    }d[100050];

    这里面顺便定义了一个10万数量级的数组。

     

     

    快速排序的函数:

    void qsort(int s, int e)

    {

      dian t;

      int l, r;

      if (s >= e) return ;

      l = s;

      r = e;

      t = d[s];

      while (l < r)

      {

        while (l < r && d[r].j1r >= t.j1r) r--;

        d[l] = d[r];

        while (l < r && d[l].j1r <= t.j1r) l++;

        d[r] = d[l];

      }

      d[r] = t;

      qsort(s, r - 1);

      qsort(r + 1, e);

    }

    然后就是逐渐退出和更新半径的过程了:

    r = d[n].j1r;

        m2 = 0;

        for (i = n ; i >= 1 ; i--)

        {

            if (d[i].j2r > m2) m2 = d[i].j2r;

            tr = d[i - 1].j1r + m2;

            if ( r > tr ) r = tr;

        }

    最后r即为最小消耗。

    四、三国游戏

    从思想实现来说,还是一道枚举加贪心的题目。因为,“人”在和计算机斗争的过程中,双方都无法取得最大的默契配合值。所以,“人”只能够去考虑次大最优值,但是,为了能够骗过计算机,我们选择的,只能够是跟我们选择的第一位武将配合值次大的那位武将(如果不是一个的话也会被计算机破坏掉)。所以,就要求我们必须,找到,每行中次大值最大的那个值(挺扰人的)。当然,这个提法是建立在我们有了题目中展示的那个矩阵之后。你要注意,题目给你的值是这个矩阵的右半边,你需要把左半边的值自己不上,才能够按照行去枚举(当然,做完后,如果你想按照列,也没有关系,因为全都是对称的,对吧)。

    当然,可能有同学考虑到了0的难问题。很遗憾,这道题肯定不会出现零。因为,计算机只能破坏“人”,而且,我也想到了骗他的方法,计算机必输无疑。

    下面看一下程序的核心部分:

    输入部分:

        for (i = 1 ; i < n ; i++)

          for (j = i + 1 ; j <= n ; j++ )

            cin >> s[i][j];

    对称复制部分:

        for (i = 2 ; i <= n ; i++)

          for (j = 1 ; j < i ; j++)

            s[i][j] = s[j][i];

    找每行次大值中的最大值部分:

        for (i = 1 ; i <= n ; i++)

        {

          t1 = t2 = 0;

          for (j = 1 ; j <= n ; j++)

          {

            if (s[i][j] > t1) t1 = s[i][j];

            if (t1 > t2) swap(t1, t2);

          }

          if (t < t1) t = t1;

        }

    很明显,t为每行次大值(t1)的最大值。也就是我们的所求。

    本题,最重要的是想到解决的方法,一旦方法确定,程序实现,相当简单。也望各位选手能够逐渐提高自己的思维方式。让自己的能力逐渐提升。

     

    没想到这么快写出解题报告,其中不足之处,望看到的高手不吝赐教。也希望我的学生们可以更快的提高自己。能够更好的为将来的发展早做准备。

    如您愿意给我发邮件,欢迎发邮件至:fatship@163.com与我交流。

    作 者:王祺磊 文章来源:http://fatship.blog.163.com
    发表评论打印文章
    相关文章
         
      主管单位: 北京市科学技术协会  北京市教育委员会
      主办单位: 北京市科学技术协会青少年工作部
      承办单位: 北京电脑天地学校
      竞赛办公室电话: 84649879  联系人:梁  晨  电子信箱:bjnoi@bjkp.gov.cn