Acm课程总结
Posted sdau_LiangWei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Acm课程总结相关的知识,希望对你有一定的参考价值。
ACM课程总结
一个学期又过去了,这个学期的ACM是我觉得最难的学科,尽管最难,但是我觉得最有用的还是它了,虽然并没有掌握这四个专题,但是在做题的过程中对于思维的锻炼确实是非常有用的,受益匪浅。对于这一篇课程总结,我将从这四个专题去进行总结,这其中有什么不对的地方还望老师予以指正。现在对图的印象最深,先从图说起吧。
图的有关算法
这这个专题里面我们主要是跟图打交道。在整个大二,可以说一直都在和图打交道,离散数学离不开图,数据结构离不开图,acm当然也离不开图。图真的是非常重要啊,它深入到我们生活的各个方面,图在我们的生活中有着极大的用处,像一些网络线缆的铺设,还有修路的最短长度或是最少费用,这些问题在题目中都反映了出来。
在这个专题里面,树和图是这个专题的全部。对于树,我们的目的是给出一棵树,然后根据要求求出这棵数的最小生成树,这里面主要有prim算法和kruscal算法,我个人认为kruscal比prim算法更加容易理解和掌握,kruscal用的是并查集进行点的合并,这个还是非常方便的,prim算法要用到队列,这无形之中就增加了算法的理解难度,在这里主要谈一下kruscal算法。这个算法的基本思想就是
1.对所有边从小到大排序;
2.依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去;
3.直到生成树中有了n-1条边,即告终止。
此算法的时间复杂度O(eloge),而prim的时间复杂度是O(n2),从这点来看,前者的效率更高的。
这这个专题里面,前几个题我都是用并查集+kruscal去解的,这类题目的一般解题步骤就是首先要建立数据模型,边如何存储,节点如何存储,像这其中有一道是关于二维平面图的连点最短距离的问题,这上面的点是采用二维坐标来表示的,跟之前的直接是一维整形数组去存储边就完全不一样了。建立数据模型之后,将边的权值按照规则去排序,一般都是从小到大排序,然后把每个节点都当作一棵独立的树,之后用并查集的方法,不在一棵树中的边合并,加上权值。这样直到所有的节点都在一棵树中。算法结束。
求最小生成树的问题相比单源点最短路径是这个专题里面比较简单的了,对于单源点最短路径问题,这个就牵扯到了图。求单源点最短路径的题我做的不多,这其中只用了spfa算法和dijkstra算法,我觉得后者的理解上虽然还算可以但是代码太复杂了,一不注意就晕了,并且这个算法对于权值为负数的时候是不适用的,局限性太大,因此布建议大家用这个算法。相比之下,spfa算法的性价比就很高了,用上个队列,建立起数据模型,一切就搞定了,容易理解,而且局限性小。
SPFA 其实就是Bellman-Ford的一种队列实现,减少了冗余,即松驰的边至少不会以一个d为∞的点为起点。
算法:
1.队列Q={s}
2.取出队头u,枚举所有的u的临边 .若d(v)>d(u)+w(u,v)则改进 ,pre(v)=u,由于d(v)减少了,v可能在以后改进其他的点,所以若v不在Q中,则将v入队。
3.一直迭代2,直到队列Q为空(正常结束),或有的点的入队次数>=n(含有负圈)。
一般用于找负圈(效率高于Bellman-Ford),稀疏图的最短路。
Spfa算法通俗点来说就是将起点入队,然后元素s出队,遍历所有的节点,之后找出和s有关联的节点,判断起点到这个节点的长度和起点到s的长度+s到这个节点的长度哪个小,若前者小,不做处理,后者小的话,修改距离,同时这个点入队,这样一直循环下去,知道队列元素为空。
就我个人而言,可能是因为之前就学过的原因,这个专题还是相对容易理解的。
贪心算法
贪心算法是什么呢?官方的定义是这样的:在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。通俗点来说,所谓贪心算法,就是在求解一个问题的过程中,在解决这个问题的每一步中,依据题目所规定的原则,依据某种算法去得到这一步的最优解,然后每一步都这样做,直到问题解决,最后的解一般就是整个问题的最优解。这里为什么要用“一般”呢?从贪心算法的定义可以看出,贪心算法不是从整体上考虑问题,它所做出的选择只是在某种意义上的局部最优解,而由问题自身的特性决定了该题运用贪心算法可以得到最优解。因此有的题目虽然每一步得到的都是最优解,但或许最后得到的问题的解并不是最优解,这一点需要我们根据问题自身的特性去判断。比如,可以自己去分析问题的本质来判断是否符合贪心算法,亦可自己通过测试一些例子来判断是否符合贪心算法。对比来看,如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。
因此,总的来说,贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,希望得到结果是最好或最优的算法。贪心算法是一种能够得到某种度量意义下的最优解的分级处理方法,通过一系列的选择得到一个问题的解,而它所做的每一次选择都是当前状态下某种意义的最好选择。即希望通过问题的局部最优解求出整个问题的最优解。这种策略是一种很简洁的方法,对许多问题它能产生整体最优解,但不能保证总是有效,因为它不是对所有问题都能得到整体最优解。
如果说我们已经确定当前问题适合与用贪心去处理,那么接下来我们的任务就是我们该如何选择贪心标准,即该用怎样的规则去筛选出最优解。
另外需要注意一点,假如我们用贪心去解决问题,那么我们一定要明白,贪心算法懂得每一步结果都对最终结果产生直接影响。贪心是在解决每一个子问题的时候给出每个问题的最优解,对每个问题的解决方案都做出选择,是不能回退的,因此我们在选择贪心标准的时候一定要慎重,标准选好了事半功倍,不然可就是事倍功半了。
综上:利用贪心策略解题,需要解决两个问题:
(1)该题是否适合于用贪心策略求解;
(2)如何选择贪心标准,以得到问题的最优/较优解。
那么贪心的具体求解过程是什么样呢?
使用贪心算法求解问题应该考虑如下几个方面:
(1)候选集合A:为了构造问题的解决方案,有一个候选集合A作为问题的可能解,即问题的最终解均取自于候选集合A。
(2)解集合S:随着贪心选择的进行,解集合S不断扩展,直到构成满足问题的完整解。
(3)解决函数solution:检查解集合S是否构成问题的完整解。
(4)选择函数select:即贪心策略,这是贪心法的关键,它指出哪个候选对象最有希望构成问题的解,选择函数通常和目标函数有关。
(5)可行函数feasible:检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。
总的来说,贪心算法对于一些问题无疑是最方便的算法,在解决一些问题的时候我们首先要考虑的应该是这道题能否去用贪心算法,在确定了这道题可以用贪心的时候,我们下个时候该解决的是贪心原则怎么去选择。选好了贪心原则,那么问题就迎刃而解了。我记得在做练习一的时候题目都有一个小技巧吧应该说,用的时候stl中排序函数sort,用了现成的函数去做排序的工作比我们再写一个函数去进行排序要方便的多。通过这个恶,我也感觉到stl真的是方便。
搜索
贪心算法结束以后,我们就进行了第二个专题,搜索。在这个专题里面,我们主要学到了一些搜索的算法像二分搜索、三分搜索、深度和广度优先搜索。搜索在写一些程序的时候是必不可少的。搜索算法是利用计算机的高性能来有目的地穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。
相比于单纯的枚举算法有了一定的方向性和目标性。算法是在解的空间里,从一个状态转移(按照要求拓展)到其他状态,这样进行下去,将解的空间中的状态遍历,找到答案(目标的状态)。
二分搜索算法,这个算法应该说是这个专题里面最简单的了,我个人认为这个算法就是采取一种极限的思想,不断把搜索区间进行分半查找,这样每次就可以舍弃掉一半的元素,这个算法还是效率很高的,需要注意的是,我们在二分查找的时候,是建立在已经把待查找区间按照某种规则排序之后才进行的。二分算法就是用待查找值x与待查区间的中间值m进行比较,看x与m的大小关系,若x=m,则查找结束;若x>m,说明x在下半区间;若x<m,说明x在上半区间,这样一直进行下去,知道找到X为止。
三分搜索算法,对于三分搜索算法,我觉得这是在二分搜索上的一种改进,效率比二分更高,这个主要是用于求一些函数的极值或临界点,在这一点上看,三分与数序的关联更大。
至于深度和广度优先搜索,我觉得这四个专题里面最难的就是BFS和DFS,反正我个人是这么认为的。因此我对这两个算法的理解也不是很深。
广度优先搜索(BFS),基本思想:从初始状态S 开始,利用规则,生成所有可能的状态。构成的下一层节点,检查是否出现目标状态G,若未出现,就对该层所有状态节点,分别顺序利用规则。生成再下一层的所有状态节点,对这一层的所有状态节点检查是否出现G,若未出现,继续按上面思想生成再下一层的所有状态节点,这样一层一层往下展开。直到出现目标状态为止。广度优先搜索是利用队列来完成的,因为它是一层一层的进行搜索,先将一层全部扩展,然后再进行下一层,这样利用队列先进先出的性质恰好可以完成这个任务。
它的具体过程是这样的:
1 每次取出队列首元素(初始状态),进行拓展,取出队首元素的时候进行判断
2 然后把拓展所得到的可行状态都放到队列里面
3 将初始状态删除
4 一直进行以上三步直到队列为空。
深度优先搜索(DFS),基本思想:从初始状态,利用规则生成搜索树下一层任一个结点,检查是否出现目标状态,若未出现,以此状态利用规则生成再下一层任一个结点,再检查,重复过程一直到叶节点(即不能再生成新状态节点),当它仍不是目标状态时,回溯到上一层结果,取另一可能扩展搜索的分支。采用相同办法一直进行下去,直到找到目标状态为止。
深度优先是用栈来完成的,因为它必须遍历完某个状态的全部的子状态才能对同一层中下一个状态进行遍历,这符合栈的先进后出的特性。
它的具体实现过程是这样的:
1 每次取出栈顶元素,对其进行拓展。
2 若栈顶元素无法继续拓展,则将其从栈中弹出。继续1过程。
3 不断重复直到获得目标状态(取得可行解)
或栈为空(无解)。
这差不多就是整个专题二的内容,这是我学的最差的专题,所以理解有限。
动态规划
在我学这个专题的时候我就觉得动态规划的基本思想和贪心原则的差不多,动态规划就是解决多决策问题的一种方法。那么什么是多阶段决策问题呢?多阶段决策问题:如果一类问题的求解过程可以分为若干个互相联系的阶段,在每一个阶段都需作出决策,并影响到下一个阶段的决策。多阶段决策问题,就是要在可以选择的那些策略中间,选取一个最优策略,使在预定的标准下达到最好的效果。通俗的说,就是问题分多个阶段,然后每个阶段有多个可选择的策略,然后我们就是要选取最优的策略。凑个思想上来看,这一点和贪心原则可以说是一模一样的。不论初始状态和第一步决策是什么,余下的决策相对于前一次决策所产生的新状态,构成一个最优决策序列。最优决策序列的子序列,一定是局部最优决策子序列。
包含有非局部最优的决策子序列,一定不是最优决策序列。从这一点来看,那就和贪心原则有差别了。我们都知道,对于有些问题是不能用贪心原则,假如我们在对待这些问题的时候用了贪心原则,那么虽然我们每一步的结果都保证是最优的,但是有时候得出来的结果并不是问题的正确答案。动态规划的指导思想就是在做每一步决策时,列出各种可能的局部解
依据某种判定条件,舍弃那些肯定不能得到最优解的局部解。以每一步都是最优的来保证全局是最优的。
在动态规划这个专题中,我觉得要想做好题目,最重要的是要找到每个题目的状态转移方程,当然这是思想的方面,至于技术方面,我觉得一定要熟练掌握递归,这个是写这些题目的根本,而且,我觉得动态规划和数学的联系很密切,像一些数学方面的递推公式在这方面都可以得到应用,我记得这个专题里面就有好几道这样的题目,对于像这样的类似的题目,只要我们找到其中的公式,代码就非常简单了,有的就只有几行,可以说就是事半功倍了。然后,这个专题中,涉及到一些背包问题,像01背包,完全背包还有分组背包,对于这3种问题,我觉得最难的不是代码,二是我们遇到问题的时候能否想起来用它这才是最根本的,特别是分组背包,很难在实际做题的时候想到去用它,这三种背包问题的状态转移方程都已经总结好了,这里就不再特别说明了,我们还是要好好想想这3中问题的应用场景,实际问题中好好想想应该用哪一种。还有个经典的是求最长自序列问题,这类题目用递推,一个字符一个字符的比较,直到递推结束,还有,要了解自序列和子串的区别,不要混淆了。
眼看这一学期马上就结束了,意味着acm也要结课了,回想起刚开学的时候,老师的一番豪言壮语让我当时真的是下定了决心要在acm这条路上一条道走到黑,那时候想我以后就专心钻研acm,以后出去参加省赛、区域赛等等,当时真的是无知啊。这四个专题走过来发现,acm真的不是一般人可以做的。我们是先学一个专题然后做题目,因为我们知道这个专题的题目肯定都是用刚学的方法,所以做起来相对容易一些。但是如果说不告诉我们这些题都是哪个专题的,随机出我们学过的专题的题目,我相信大部分人还是不知道用什么方法会做。
走过了四个专题,最难的我觉得应该就是搜索那里了,然后最简单的应该就是图这里了,也可能是因为搜索那个专题确实没有认真做题。四个专题走过来,虽然与当初自己的豪情壮志渐行渐远,但是这其中确实锻炼了我的思维能力,明白了一些算法也确实提高了我的编程能力,我觉的这就是收获。虽然以后可能再也不会碰acm了,但是和同学们一起刷题,为了一道题憋了我一晚上的这种经历我终身难忘。
以上是关于Acm课程总结的主要内容,如果未能解决你的问题,请参考以下文章