学习ACM一个学期,感触还是蛮多的,因为最近接触最多的都是JavaWeb,css,这些。除了一开始学的c语言的时 候,其余大部分时间用的都是封装好的类,一度很疑惑,既然已经有封装好的链表,迭代,栈和队列。。我为什么还 要学习,直接用就好了。随着时间增长,觉得自己的编程能力直接下降,加上处于迷茫阶段,不知道自己的方向在哪 里也没有什么明确目标,但是我一直觉得 当你不知道做什么的时候,就去学习。加上学校要求修学分于是就选了 ACM。。。
初衷没有太大的期望和抱负,只是觉得多学一些知识不管是考研还是就业都是有帮助的,编程这件事真的是不练就忘得快啊,无语。。像J2EE,HTML这种前端重点在于框架的构建和细节的填充,但我们学的关注点应该是算法加语言,作为一个大三生,一开始学习没啥感觉,虽然有的知识有点遗忘,但是老师一讲还是可以回忆起来的,因为不是抱着参赛拿奖的想法去的,所以积极性并没有像其他人那么高,刷题什么的也就不考虑了,加上只是大都学过,链表,栈,队列,图论都在数据结构的课里学过,所以老师讲起来也是知道是什么,但是老师讲的大部分都是算法的思想,这个很喜欢。
当然,事物都有两面性学习ACM又优点也会有弊端,下面是对我的一些优点和弊端:
1.首先,编程能力肯定有所提高,细节也会有所注意,因为ACM注重短时间的正确性,所以无疑的锻炼了你的思维缜密性和知识的掌握程度。
2.对于语言算法的理解能力提高,毕竟算法还是核心嘛,好的算法是事半功倍的,对算法的理解掌握程度往往决定你的程序的性能和稳定性。
3.对于一个英语着急的计算机孩子来说,学习ACM两者皆抓。毕竟题都是英文的,都是泪。
缺点嘛就是时间和精力的问题,它学要长时间的积累,这个学期课多还是专业课来说,用于刷ACM题的时间很少,还要把时间分配好用于其他课程的程序设计,另一方面,它需要知识的积累的智商,对我来说智商是硬伤啊。而且ACM要求完全正确,但是真实中的工程开发难免会有bug的。
算法总结:
1.先学的是贪心算法,贪心是一种最优解思想,这种最优是在局部的情况进行的,就是说在每一步中,都要保证这是当前情况下的最优解。贪心算法要注意当前状态不会对之后的状态产生影响,且保证当前选择的转态是最优的。
贪心的问题一是不能保证最后的结果是最佳的,因为它不是从整体出发,而是局部出发;问题二是不能用来求最小和最大解,问题三是其应用范围有一定约束性。
步骤:建立数学模型描述问题----->将问题分为若干子问题--------->对子问题求解,得到局部最优解------>将局部最优解和为整体最优解
要注意贪心策略的选择,先考虑问题是否适合贪心,是否可以通过局部最优得到全局最优。
常见的贪心:(1)背包问题(2)最优装载问题 (3)活动安排问题(4)删数问题
2.第二个专题是搜索问题,有目的地穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。分为广度搜索,深度搜索,二分法,三分法。
(1)深度优先搜索(dfs):结合递归思想或利用栈,下一次的扩展点事子节点中的一个。适合用于节点较多时使用,可以用回溯算法代替。
(2)广度优先搜索(bfs):利用队列,下一个扩展点是兄弟节点的一个。适合用于求最优解。寻找深度小,但是耗费量大。
(3)二分法:在一个单调有序的集合中查找,每次判断集合中间值与目标大小,调节集合上下界,重复多次直到 找到目标元素。快速,查找次数少,平均性能好,适合有序数据。
(4)三分法:函数求导在进行二分法。
3. 动态规划:
动态规划:递推+重复子问题。
后一个状态通过前一个状态确定,保证了步步最优,从而保证了全局最优。
简单dp的状态比较容易表示,转移方程也比较好想。
通常包括四种类型:递推,背包,LIS(最长递增序列),LCS(最长公共子序列)
递推一般形式比较单一,从前往后,分类枚举就行。
其中背包又分为01背包,分组背包,完全背包,多重背包。
动态规划是解决多阶段决策问题的一种方法;
多阶段决策问题:如果一类问题的求解过程可以分为若干个互相联系的阶段,在每一个阶段都需作出决策,并影响到下一个阶段的决策。
动态规划问题的一般解题步骤
1、判断问题是否具有最优子结构性质,若不具备则不能用动态规划。
2、把问题分成若干个子问题(分阶段)。
3、建立状态转移方程(递推公式)。
4、找出边界条件。
5、将已知边界值带入方程。
6、递推求解。
最大公共子串问题(两个字符串中最大的公共部分)
设第一个串为a,长度为n,第二个串为b,长度m。那么最长的子序列长度为f(n,m),当a[n]=a[m]时,f(n,m)=1+f(n-1,m-1),否则f(n,m)=max(f(n-1),f(m-1))
01背包问题:有N件物品和一个容量为V的背包。第i件物品的大小是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
状态方程:01背包体积要从v---0
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+val[i]);
dp[j]=max(dp[j],dp[j-v[i]]+val[i]);
完全背包问题:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
完全背包与01背包唯一区别就是01背包的物品每个只能取一次,而完全背包可以取无限次
dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+val[i])
dp[j]=max(dp[j],dp[j-v[i]]+val[i]);
多重背包问题:有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
用DP[I][J] 表示前I件物品放入一个容量为J的背包可以获得的最大价值。DP[I][J]=MAX(DP[I-1][J],DP[I-1][J-K*C[I]]+K*W[I])
K的范围,0<=K<=N[I] && 0<=K*C[I]<=J
4.图:二元组<V,E> 称为图(graph)。 V为结点(node)点(vertex)集。 E为图中结点之间的边的集合。
ACM中不用链表表示图,而是用邻接矩阵和邻接表来表示图中定点与边的关系。
子图:当图G'=(V',E')其中V‘包含于V,E’包含于E,则G'称作图G=(V,E)的子图。每个图都是本身的子图。
生成子图:指满足条件V(G') = V(G)的G的子图G。
度:一个顶点的度是指与该顶点相关联的边的条数,顶点v的度记作d(v)。
入度和出度:对于有向图来说,一个顶点的度可细分为入度和出度。一个顶点的入度是指与其关联的各边之中,以其为终点的边数;出度则是相对的概念,指以该顶点为起点的边数。
在有向图中,通常将边称作弧,含箭头的一端称为弧头,另一端称为弧尾,记作<vi,vj>,它表示从顶点vi到顶点vj有一条边。若有向图中有n个顶点,则最多有n(n-1)条弧,若无向图中有n个顶点,则最多有n(n-1)/2条弧。
最小生成树:图的所有生成树中,权值最小的树。
求最小生成树的常用算法:
(1)prim算法:
任取一个顶点加入生成树;
在那些一个端点在生成树里,另一个端点不在生成树里的边中, 取权最小的边,将它和另一个端点加进生成树。
重复上一步骤,直到所有的顶点都进入了生成树为止。
(2)kruskal算法
对所有边从小到大排序;
依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去;
直到生成树中有了n-1条边,终止。
最短路问题:
1)单源最短路径
(1)dijkstra算法:解决了有向图G=(V,E)上带权的单源最短路径问题,效率高,局限性是对于含负权的图无能为力。
(2)bellman-ford算法:在求解过程中,每次循环都要修改所有顶点的dist[ ],源点到各顶点最短路径长度一直要到Bellman算法结束才确定下来。对于所有最短路长存在的图都适用。
(3)spfa算法:Bellman-Ford的一种队列实现,减少了冗余。对于最短路长存在的图都适用,无论是否存在负权。它的编程复杂度也很低,是性价比极高的算法。
2)每对顶点的最短路径
floyd-washall算法
以上是这个学期学习到的ACM知识点总结,基于整个学习过程又让我重新了解和掌握很多知识点,在国内,大部分用的是Java,但在国外,人们更常用c。ACM的学习让对于编程的严谨性更加重视,虽然真实的开发过程中对于bug有一定的包容性,但我们仍然要尽可能的完善系统,确保系统的安全系数。虽然对于目前前端开发来说,人们更多的是关注框架的建设和细节的填充,但是算法思想的学习和理解同样不能忽略,这有利于我们工作的更好完成。