ACM学期总结
Posted SDAU_ZG
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM学期总结相关的知识,希望对你有一定的参考价值。
从大一就开始对这个东西感兴趣,当时其实并不知道ACM是什么,当时学习想法也很简单,就是想用来提高自己的编程能力。
接触ACM已经有了一学期的时间,说时候ACM真的很难学,从一开始的激情满满,到后来的索然无趣。ACM是十分花费时间的东西,平均下来我几乎每道题的的时间差不多就有两个小时,虽然会很打击人信心,但是在某些方面自己的能力确实提高了不少,无论自己在考虑问题,分析问题上,还是对于语言的认识上,考虑的比之前更加全面,分析的比之前更加透彻,ACM便是运用已有的算法,解决更加接近于现实的问题。刷题只是学习的一种手段,做多少题不是最重要的,重要的是我们学到了什么,这是需要我们去总结的,正如好记性不如烂笔头,将精华记录下来,在忘记的时候查看,才能快速捡起。
记得刚开始接触ACM,首先听到的STL,当时自以为C学的不错,但是对于STL标准库完全不知道是什么,但是接触后才发现是一个已经定义好的标准模板库,包含了队列,链表,数组等数据结构和与之相关联的操作。这里明白了什么是所谓的泛型,什么是迭代器。
所学算法总结:从比较简单的贪心:一种求最优解的算法,更是一种思想。在构造最优解的每一步过程中,都在构造局部最优解,解这类问题关键是找到最优策略,并通过局部最优解达到全局最优解的目的。
使用贪心算法求解问题应该考虑如下几个方面:
(1)候选集合A:为了构造问题的解决方案,有一个候选集合A作为问题的可能解,即问题的最终解均取自于候选集合A。
(2)解集合S:随着贪心选择的进行,解集合S不断扩展,直到构成满足问题的完整解。
(3)解决函数solution:检查解集合S是否构成问题的完整解。
(4)选择函数select:即贪心策略,这是贪心法的关键,它指出哪个候选对象最有希望构成问题的解,选择函数通常和目标函数有关。
(5)可行函数feasible:检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。
常见的
背包问题(给定一个载重量为M的背包,考虑n个物品,其中第i个物品的重量 ,价值wi (1≤i≤n),要求把物品装满背包,且使背包内的物品价值最大。)
最优装载问题(有一批集装箱要装上一艘载重量为c的轮船,其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船)
删数问题(n位数a可表示为x1,x2,x3,xi...xn,要删去K位数,使得剩下的数字组成的整数最小)
多处最优服务次序问题(设有n个顾客同时等待一项服务,顾客i需要的服务时间为ti,1≤i≤n,共有s处可以提供此项服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n个顾客等待服务时间的总和除以n。)等等。
接着是搜索算法:利用计算机的高性能来有目的地穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。常用的深度优先搜索(DFS)和广度优先搜索(BFS),深度优先搜索主要结合递归思想(利用栈也可以),广度优先搜索主要结合着队列。其搜索一般解决和图相关的问题。搜索算法已经不仅仅局限于自身数据结构,并且用到了辅助空间来标记一些中间结果,这对于新手来说,是重要的具有启发性的,但不能否认,这很难。
广度优先搜索(BFS):基本思想:从初始状态S 开始,利用规则,生成所有可能的状态。构成的下一层节点,检查是否出现目标状态G,若未出现,就对该层所有状态节点,分别顺序利用规则。
生成再下一层的所有状态节点,对这一层的所有状态节点检查是否出现G,若未出现,继续按上面思想生成再下一层的所有状态节点,这样一层一层往下展开。直到出现目标状态为止。
其搜索结构类似于树结构中的层序遍历
深度优先搜索(DFS)
基本思想:从初始状态,利用规则生成搜索树下一层任一个结点,检查是否出现目标状态,若未出现,以此状态利用规则生成再下一层任一个结点,再检查,重复过程一直到叶节点(即不能再生成新状态节点),当它仍不是目标状态时,回溯到上一层结果,取另一可能扩展搜索的分支。采用相同办法一直进行下去,直到找到目标状态为止。
其搜索结构类似于树结构中的先序遍历
可以利用深度优先搜索(DFS)求0-1背包问题(向一定空间的背包中放物品,不同的物品价值不一样,求最大价值)
滑雪问题;
石油分布(从一个位置打油,地下连在一块的为一块油田,求地下油田最多连结多少块石油)
n皇后问题(在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。你的任务是,对于给定的N,求出有多少种合法的放置方法。)等等
算法分析:
BFS:对于解决最短或最少问题特别有效,而且寻找深度小,但缺点是内存耗费量大(需要开大量的数组单元用来存储状态)。
DFS:对于解决遍历和求所有问题有效,对于问题搜索深度小的时候处理速度迅速,然而在深度很大的情况下效率不高。
可见无论是BFS还是DFS,在一定情况下都是需要进行不断的优化才能较好的使用。(判重,枚举,深入)
二分查找算法(快速的查找的算法,适用于有序数据)
简单定义:在一个单调有序的集合中查找元素,每次将集合分为左右两部分,判断解在哪个部分中并调整集合上下界,重复直到找到目标元素。
时间复杂度:O (logn),优于直接顺序查找O(n)
关键性代码:
while(high - low > 1.0e-6)
{ mid = (high + low)/2;
if(Caculate(mid)<x) low=mid;
else high=mid;
}
问题描述:
题目大意:有f+1个人分n块披萨,每个人要求分得的面积一样,且披萨只能被切开而不能重新组合,求每个人能分到的最大面积v。
通过二分不断划分,最后得到结果。
三分(二分用于线性求解,三分为曲线求解):
当需要求某凸性或凹形函数的极值,通过函数本身表达式并不容易求解时,就可以用三分法不断逼近求解。
关键性代码部分:
类似二分的定义Left和Right
mid = (Left + Right) / 2
midmid = (mid + Right) / 2;
如果mid靠近极值点,则Right = midmid;
否则(即midmid靠近极值点),则Left = mid;
总结:对于求解一些实际问题,当公式难以推导出来时,二分、三分法可以较为精确地求解出一些临界值,且效率也是令人满意的。总之,二分三分都是运用的逼近思想,通过不断切割,逐渐逼近所求结果。
经典案例:
汽车拐弯问题,给定X, Y, l, d判断是否能够拐弯。首先当X或者Y小于d,那么一定不能。
其次我们发现随着角度θ的增大,最大高度h先增长后减小,即为凸性函数,可以用三分法来求解。
这里的Calc函数需要比较繁琐的推倒公式:
s = l * cos(θ) + w * sin(θ) - x;
h = s * tan(θ) + w * cos(θ);
其中s为汽车最右边的点离拐角的水平距离, h为里拐点最高的距离, θ范围从0到90。
四:动态规划
动态规划是解决多阶段决策问题的一种方法;
多阶段决策问题:如果一类问题的求解过程可以分为若干个互相联系的阶段,在每一个阶段都需作出决策,并影响到下一个阶段的决策。
多阶段决策问题,就是要在可以选择的那些策略中间,选取一个最优策略,使在预定的标准下达到最好的效果.
最优性原理
不论初始状态和第一步决策是什么,余下的决策相对于前一次决策所产生的新状态,构成一个最优决策序列。
最优决策序列的子序列,一定是局部最优决策子序列。
包含有非局部最优的决策子序列,一定不是最优决策序列。
在做每一步决策时,列出各种可能的局部解
依据某种判定条件,舍弃那些肯定不能得到最优解的局部解。
以每一步都是最优的来保证全局是最优的。
与贪心不同,贪心是通过求局部最优来获得全局最优,但是局部最优不一定保证全局最优。
而动态规划,后一个状态通过前一个状态确定,保证了步步最优,从而保证了全局最优。
动态规划的几个概念
阶段:据空间顺序或时间顺序对问题的求解划分阶段。
状态:描述事物的性质,不同事物有不同的性质,因而用不同的状态来刻画。对问题的求解状态的描述是分阶段的。
决策:根据题意要求,对每个阶段所做出的某种选择性操作。
状态转移方程:用数学公式描述与阶段相关的状态间的演变规律。
动态规划问题的一般解题步骤
1、判断问题是否具有最优子结构性质,若不具备则不能用动态规划。
2、把问题分成若干个子问题(分阶段)。
3、建立状态转移方程(递推公式)。
4、找出边界条件。
5、将已知边界值带入方程。
6、递推求解。
动态规划:递推+重复子问题。
动态规划算法的效率主要与重复子问题的处理有关
常见问题模型:
1,最大公共子串问题(两个字符串中最大的公共部分)
关键思想代码:
设第一个串为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]。求解将
哪些物品装入背包可使价值总和最大。
关键思想:用DP[I][J] 表示前I件物品放入一个容量为J的背包可以获得的最大价值。则
DP[I][J]= DP[I-1][J] ,J<C[I]
MAX(DP[I-1][J],DP[I-1][J-C[I]]+W[I]) , J>=C[I]
01背包问题是基本的背包问题,它包含背包问题中设计问题,方程的最基本思想。另外别的背包问题往往也可以转化为01背包问题求解。谷一定要体会上面思路的得出方法,状态转移方程的意义,以及空间复杂度如何优化。
完全背包问题:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第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]) 0<=K*C[I]<=J
将01背包问题基本思路加以改进,得到这样一个清晰的方法,说明01背包问题的确是很重要的,可以推及其他类型的背包问题。但我们还是要试图改进这个算法。
将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。多重背包问题:
有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
完全背包问题也是一个相当基础的背包问题,他有两个状态转移方程。
动态规划大致设计到三种背包问题,其他问题也可以归于这三类。动态规划关键在于理解当前状态选择通过上一状态决定。动态规划需要辅助空间记录过程中的一些状态,比之搜索,其算法构建更加复杂。
五:图
这一章比较简单对我来说,毕竟在数据库结构中,我们接触了图的基本感念和图的建立,遍历。图的最短路径,最小二叉树等等。
关键概念:
图:二元组<V,E> 称为图(graph)。 V为结点(node)点(vertex)集。 E为图中结点之间的边的集合。
顶点的度
无向图中,一个顶点相连的边数称为该顶点的度。
有向图中,从一个顶点出发的边数称为该顶点得出度;到达该顶点的边数称为它的入度。
顶点的最大度数称为图的度数。
图的常用两种表示结构:
邻接矩阵(一个二维数组表示顶点之间的关系)
邻接表(每个顶点和其他关联顶点组成链表)
最小生成树:
最小生成树:图的所有生成树中,权值最小的树。
求最小生成树的常用算法:
prim算法
kruskal算法
prim算法的基本思想:
任取一个顶点加入生成树;
在那些一个端点在生成树里,另一个端点不在生成树里的边中,取权最小的边,将它和另一个端点加进生成树。
重复上一步骤,直到所有的顶点都进入了生成树为止。
Prim算法:
(1) 任意选定一点s,设集合S={s}
(2) 从不在集合S的点中选出一个点j使得其与S内的某点i的距离最短,则(i,j)就是生成树上的一条边,同时将j点加入S
(3) 转到(2)继续进行,直至所有点都己加入S集合。
kruskal算法的基本思想
对所有边从小到大排序;
依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去;
直到生成树中有了n-1条边,即告终止。
算法的时间复杂度O(eloge)
Kruskal算法:
将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边。
最终得到的结果就是最小生成树。
并查集
最短路问题
单源最短路径
bellman-ford算法
spfa算法
dijkstra算法
每对顶点的最短路径
floyd-washall算法
Dijkstra算法解决了有向图G=(V,E)上带权的单源最短路径问题,但要求所有边的权值非负。Djikstra算法中设置了一顶点集合S,从源点s到集合中的顶点的最终最短路径的权值均已确定,算法反复选择具有最短路径估计的顶点u∈V-S,并将u加入S中,对u的所有出边进行松弛。
dijkstra算法和 floyd-washall算法便是在prime算法和Kruskal算法基础上的改造时间复杂度O(n^3)等。
说到底,这学期的ACM并没有投入太多的时间和精力,可能是这学期的课程比较多些,总是感觉自己的时间安排的很忙,但是ACM作为编程的基础,我认为其中的很多思想及其算法是十分重要的,在我看来学习ACM可以让编程能力更上一层楼,可以让我们更深层次的认识编程是什么,可以让我们更加接近于现实的去思考问题,所以关于ACM有时间还是要继续研读的,其中的解决问题的思想,涉及的不同算法,构造的不同数据结构,这对于我们解决实际问题有着重要的指导意义,对于我们以后参与工作有着巨大的帮助。就像设计模式一样,算法的设计并非是刷两道题就能学会的,更多的应该是一种考虑问题的思想。
以上是关于ACM学期总结的主要内容,如果未能解决你的问题,请参考以下文章