ACM程序设计结课总结



ACM程序设计课程总结


  为期一个学期的acm程序设计终于结课了,听说很多本校学习acm算法程序设计的同学,都是从大一开始跟着老师在实验室开始学习的,对于课堂上讲的东西,多数他们已经会了,而且一些全国或者省的acm比赛都参加过了,作为一个半路出家的学生而言--我,确实觉得汗颜,人外有人。Acm程序设计,在我的理解而言,就像是计算机中的高数,数学素养,不光是靠一个人的天赋,跟多的是努力!坚持不懈的自己解决每一道题,从张掌握每一种题目的算法思想,就像是高数,看到积分,我们会想到是定积分还是不定积分,区别那种积分后对应的积分又有哪些解题方法或者解题思路可用,然后尝试是否能通。所以做acm,就像是解决一道很难得高数题,首先要掌握各种解题方法,然后凭借经验规避很多错误。做题是有时间限制的,所以在做题的时候,反应能力、应变能力都很重要,然而这些都是从题海中总结出来的。爱因斯坦说过,成功就是1%的天赋加上99%的努力!

  还有一点体会最深的是,acm编程跟一般课堂写程序的不同,acm你设计的算法,编程语言确实就是工具,你依赖的工具,而课堂作业等,编程就是让你熟悉一门语言。算法设计是升华!

  好了,废话这么多,该做点学期学习总结了!

  首先,什么是acmACM国际大学生程序设计竞赛(英文全称:ACM International Collegiate Programming ContestACM-ICPCICPC)是由国际计算机学会(ACM)主办的,一项旨在展示大学生创新能力、团队精神和在压力下编写程序、分析和解决问题能力的年度竞赛。经过近30多年的发展,ACM国际大学生程序设计竞赛已经发展成为最具影响力的大学生计算机竞赛。赛事目前由IBM公司赞助。

  这个学期,总共讲了四个专题,分别是贪心算法专题,搜索专题,动态规划专题和图算法专题

  (1首先,介绍一下贪心算法专题,贪心算法的基本思想是总是做出在当前看来是最优的选择,即找出整体当前中每个小的局部的最优解,并且将所有的这些局部最优解合起来形成整体上的一个最优解,也就是说贪心算法并不是从整体最优考虑的,它所做出的选择只是在某种意义上的局部最优选择,能够使用贪心算法的问题必须满足下面两个性质:

  1. :整体的最优解,可以通过局部最优解得出。

  2. :一个整体可以分割成多个部分,每一部分都可以生成一个最优解。

      从贪心算法的定义可以看出,贪心算法不是从整体上考虑问题,它所做出的选择只是在某种意义上的局部最优解,而由问题自身的特性决定了该题运用贪心算法可以得到最优解。如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。

      比如活动安排问题,即有许多个活动,每个活动都有一定的时间,在一个时间轴上,每个活动的开始时间,结束时间都不同,问如何安排活动才能可以看到参加到的活动?这个问题是贪心算法入门的基础题,每个活动都是整体的一部分,如何筛选那?我们可以整理一下每个活动的开始时间进行排序,然后比较第一个活动的结束时间跟下一个活动的开始时间以及结束时间如果下一个活动的开始时间大于第一个的结束,则可以,或者下一个活动的结束小于第一个活动的结束,那就将第二个活动安排为第一舍弃第一个活动,一次类推,每个局部进行比较,以此下去局部最优就是整体最优!

    bool cmp(const action &a, const action &b)

    {

    if (a.f<=b.f) return true;

    return false;

    }//重载运算符,对起始时间进行排序;

    //形参数组b用来记录被选中的活动

    void GreedySelector(int n, action a[], bool b[])

    {

      b[1] = true;     //1个活动是必选的

      //记录最近一次加入到集合b中的活动

      int preEnd = 1;

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

        if (a[i].s>=a[preEnd].f)

        {

          b[i] = true;

          preEnd = i;

        }

    }

    //进行贪心点核心算法。

      所以,贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,希望得到结果是最好或最优的算法。这种策略是一种很简洁的方法,对许多问题它能产生整体最优解,但不能保证总是有效,因为它不是对所有问题都能得到整体最优解。当然是否可以用贪心,还要看是否满足贪心的特点。

  1. 搜索算法,是这学期的第二个专题,搜索算法是利用计算机的高性能来有目的地穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。相比于单纯的枚举算法有了一定的方向性和目标性。算法是在解的空间里,从一个状态转移(按照要求拓展)到其他状态,这样进行下去,将解的空间中的状态遍历,找到答案(目标的状态)。

      状态(state)是对问题在某一时刻进展情况的数学描述,或者是数学抽象。每一个状态都会是答案的一个“可能的”解。状态的转移就是问题从一个状态转移到另一个状态,这样就可以进行搜索的一步步延伸,最后要得到的解也是其中的一个状态。那如何进行状态转移?有两种,广度优先算法跟深度优先算法!

      简而言之,广度优先算法,如果是树状结构,那就从根节点开始,一层一层的开始遍历,首先根节点,然后跟的孩子们,然后根的孩子的孩子们,一层一层的遍历下去。当然在用广度优先算法的时候,我们要用到队列,根据队列先进先出的思想,将跟放进队列,然后根出队,将其孩子依次入队,第一个孩子出队,依次将每个孩子的孩子再入队,如此往复下去。

      深度优先算法,从初始状态,利用规则生成搜索树下一层任一个结点,检查是否出现目标状态,若未出现,以此状态利用规则生成再下一层任一个结点,再检查,重复过程一直到叶节点(即不能再生成新状态节点),当它仍不是目标状态时,回溯到上一层结果,取另一可能扩展搜索的分支。采用相同办法一直进行下去,直到找到目标状态为止。深度优先会用的栈的思想,符合栈的先进后出(FILO)的性质。

     

    广度优先框架:

    While Not Queue.Empty ()

    Begin

    可加结束条件

    Tmp = Queue.Top ()

    Tmp循环拓展下一个状态Next

    If 状态Next合法 Then

    Begin

    生成新状态Next

    Next.Step = Tmp.Step + 1

    Queue.Pushback (Next)

    End

    Queue.Pop ()

    End

    深度优先框架--递归实现:

    递归实现:

    Function Dfs (Int Step, 当前状态)

    Begin

    可加结束条件

    从当前状态循环拓展下一个状态Next

    If 状态Next合法 Then

    Dfs (Step + 1, Next ))

    End

    深度优先非递归实现:

    非递归实现:

    While Not Stack.Empty ()

    Begin

    Tmp = Stack.top()

    Tmp拓展下一个未拓展的状态Next

    If 没有未拓展状态(到达叶节点) Then

    Stack.pop()

    Else If 状态Next合法 Then

    Stack.push(Next)

    End

  2. 动态规划专题:动态规划算法通常具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法十分相似,它的基本思想就是把要求的问题划分成若干个子问题,然后从这些子问题的解中得到原来的问题的解。但是与分治法不同的是,适合用动态规划求解的问题,它的子问题往往不是相互独立的。动态规划又与贪心算法有些类似,往往把一个问题的解决方案视为一系列决策的结果。但与贪心算法不同的是,每采用一次贪心算法准则就要做出一个不可回溯的决策,而在动态规划中,还要观察每个最优决策的序列中是否包含一个 最优子序列。设计动态规划法的步骤如下所示:

  1. :找出最优解的性质,并刻画其结构特征

  2. :递归的定义最优值(写出动态规划方程);

  3. :以自下而上的方式计算出最优解;

  4. 根据计算最优解时得到的信息,构造出一个最优解。

    在上面的所有步骤中,步骤(a~c)是动态规划算法的基本步骤。在只需要求出最优值的情形,步骤(d)可以省略,步骤(c)中记录的信息也比较少;若需要求出问题的一个最优解,则必须执行步骤(d),步骤(c)中的记录的信息必须足够多以便构造最优解。适合动态规划算法的问题本身具有两个非常重要的条件:最优子结构和子问题的重叠性质。从一般意义上来说,这两个性质是用来动态规划求解的基本要素。

    (a):最优子结构性质:当问题的最优解包含了其子问题的最优解时,称为该问题具有最优子结构性质。在分析问题的这个性质时,所用的方法具有普遍性。首先假设由问题最优解导出的子问题的解不是最优的,然后再设法说明在这个假设下可构造出比原问题 最优解的更好的解,从而导致矛盾。一般采用自底向上的方式递归地从子问题的最优解 逐步构造出整个问题的最优解。

    (b)重叠子问题性质:在用递归方法自顶向下求解问题时,每次所产生的子问题并不是新的问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质, 对每个子问题只计算一次,然后将其保存在一个表格中,以方便以后用到的时候查询, 从而能获得较高的解题效率。

  1. 图算法专题:

      该专题是我认为到目前为止学的最难的专题,虽然图在数据结构以及离散数学中已经学过,对于定义以及使用也很了解,但是真正用起来真是感觉太难了。

      生成树:由Gn-1条边构成的无环的子图,这些边的集合成为生成树。

      最小生成树:所有生成树中权值最小的一个边集T为最小生成树,确定树T的问题成为最小生成树问题。

    prim算法:任取一个顶点加入生成树;在那些一个端点在生成树里,另一个端点不在生成树的边中,取权最小的边,将它和另一个端点加进生成树。重复上一步骤,直到所有的顶点都进入了生成树为止。

    kruskal算法:对所有边从小到大排序;依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去;直到生成树中有了n-1条边,即告终止。将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边。最终得到的结果就是最小生成树。

    这一章,实在是很难,个人感觉,所以总结也是草草结束,见谅!

      再进行下个人总结吧,我觉得最大的收货,除了了解了这么多算法外,我认识到了,语言只是表达思想的工具,就像我们说话,小孩的时候,练习说话,练习表达思想,当长大以后,说话已经很流利了,我们就在驾驭语言这个工具来表达思想,acm程序设计就是这样的一个升华,大一刚开始学习你们语言,在练习使用,而现在,我们已经脱离了这个阶段,更多的,我们应该用他去表达思想了!在写代码过程中,有很多东西,都是在别人的代码里面学习来的,很多规则的用法,也是从中学习来的,总之,收获着的很大!

      也祝,我们山农的acm参赛者们,取得好成绩,为你们的辛勤付出,取得回报!

     

你可能感兴趣的:(ACM程序设计结课总结)