一、枚举:
枚举是最简单最基础的算法,核心思想是将可能的结果都列举出来并判断是否是解。
优点:思维简单,帮助理解问题、找规律。没头绪时
缺点:时空复杂度较高,会有很多冗余的非解(简单的枚举几乎没有利用题目中任何隐藏的特殊性质)。
运用枚举的话,首先要确认枚举的状态,或建立一个方便枚举的模型。由此知道枚举的优化主要在以下方面:
1、建立有效、巧妙的枚举模型,减少对非解的枚举。
2、如有多步操作,适当调整各步操作的顺序,使枚举高效化。
3、学习更强大的算法、数据结构、性质等等,用它们来优化枚举(滑稽)。
例题:
1、
简单的枚举所有苹果的高度。
2、
题解:
· 3、
题解:
巧妙地建立模型、利用性质。
二、搜索 :
搜索实质上就是高级的枚举。通过动态的枚举步数找到正解。搜索有两种:DFS(深度有先搜索)与BFS(广度优先搜索)
DFS:能向下走就向下走,走不了再回头。
BFS:我全都要。
对比:DFS通常时间复杂度为指数级别(剪枝不好的话),但比较节省空间;BFS的时间复杂度通常不会超过图的大小(一般每个点遍历一次,每条边遍历二次),但需要大量的空间存储状态。做题时一般优先考虑BFS,不行的话再用DFS。
细节注意:DFS有时需判断是否需要回溯到之前的状态:若当前节点会受到其“表弟/妹”节点的影响,则应保存、回溯到之前的状态;否则可以不用管它;BFS通常需要建立一个多维数组判重(有时也用map),对于一些不求最短步数而求其他最优化的量时通常会用优先队列进行扩展,但此时应注意是大根堆还是小根堆。
解题时先建立起状态的模型,如占空间太大,可考虑降温、循环利用等方法。之后再选择合适的搜索方法、搜索顺序(一些多组数据的搜索题一般可以倒着从结果状态回搜到起始状态,一遍搜索就结束)解题。
例题:
1、八数码:
题解:
*判重可用map[一个0~8的排列]判重(map可视为一个可用多种类型当下标的动态数组)
多组数据的话可从目标状态倒着搜回起始状态。
2、
题解:
由于是问经过最少的障碍物数,而对走过的步数无要求,所以先扩展不经过障碍物的所有节点,在扩展经过一个障碍物的节点,具体存储结构:二个队列循环用。
3、推箱子
题解:箱子推动的方向与 人的位置和地形 有关,而人的位置与 箱子的位置和地形 有关,便建立一个四维数组表示状态(人的位置和箱子的位置)。状态转移的方式有二个:人走、人推箱子走。由于要先确定人能从哪推箱子,所以先扩展人能推箱子的位置,再扩展箱子能到达的位置。
时间复杂度O(N*M*N*M)
4、迷宫入口
题解:
找到搜索的入手点很重要。因为大正方形左下角必放有一小正方形,我们可以从从低到高、从大正方形左下角按从左到右的顺序,找到最低的点中最靠左的尝试放小正方形来维护空余部分下表面轮廓。只要该小正方形不出界、其下表面在轮廓线的拼接处这一整段是平的,便可以放它,并继续搜索下去。
一个非常强力的剪枝:数据范围ci ≤ 10但n = 16,那么每次我们回溯后不要去重复的选择一个与这步选过的正方形中相同的正方形,这样就能通过本题了。
时间复杂度:玄学;
4、华容道
题解:
是一个相当复杂的BFS最短路题,难点在于如何构建合适的状态.我们可以设计初始状态(x1, y1, x2, y2, x3, y3),分别记录三个块的左上角位置,注意到每一维的坐标可能在(-20, 20)之间,所以状态会很多.
我们来尝试将一些状态隐藏起来,不妨强制x1, y1是当前坐标轴的(0,0),然后这样我们只需要记录另外两个点的位置就行了。因为事实上只有相对位置是有用的:如果把1号块视为不动的话,原来的3中种状态转移的方法(1号移一格,2号移一格,3号移一格)就变为:2号3号一起移一格,2号移一格,3号移一格(换了个参考系,就成功降了个维)。
注意到每一维的范围仍然是(-20, 20),所以状态数被我们缩减到了40^4就可以接受了.
时间复杂度O(40^4)
记忆化搜索:在一些DFS中的复杂度的主要来源其实是重复计算。因此第一次计算到这里时便可以保存下来,下次在算到这里时直接输出,优化了时间复杂度。
三、贪心
1、简介:
注意:贪心采取当前局部最优解,最终生成的解不一定是整体最优解。
2、例题:
1、
题解:
找不变与变化的量。
2、
第二行的Ai为任意的小于当前最大出水量的非负实数。
题解:=T,化简得(Ai*(ti-T))=0。可以把数组分成两部分:ti-T大于等于零的和小于零的。设两部分分别取出一个,为Ai、ti-T,Aj、tj-T (ti-T<0,tj-T>=0)。则总有Ai、Aj取值足够小使得满足这两个加起来=0。又用贪心思想,故可以知道两部分一定有一部分全选,另一部分选t最靠近T的那些。
3、
题解:
4、
四、二分
1、二分:在单调的待定区间里选则mid比较,确定一边无意义,每次问题规模缩小1/2。复杂度为O(log n);
2、二分答案:
例题:
1、
题解:
看似贪心,实则主要为二分答案。
2、
题解:最大值最小化问题,考虑二分答案
3、三分:求一个单峰函数的极值点。(要读出题中隐含的单峰函数关系)
例题:
1、
题解:
五、分治:
应用:快速幂(都会就不说了)
归并排序:
分:
治:
两个指针指向两区间走到哪,比较两指针所指,小的进入答案数组,并指针后移一格。若已指到区间尾,让另一个区间剩下的依次进入答案数组就行了。
例题:
1、逆序对
题解:
显然枚举铁定超时了。
考虑分治。设work(1,n)为区间[1,n]中逆序对数量,则其可由三种情况推出:work(1,mid),work(mid+1,n),当左右两部分共同出一个数组成一个逆序对。由此想到归并排序思想中相关内容:
2、
题解:
设f(n)为n按规则拆分后的数的数量,g(n)为拆完后1的数量,由题可推出f(n)=2*f(n/2)+1,g(n)=2*g(n/2)+n%2,都可用递归推出。对于区间[l,r],可用区间拆分的思想拆成若干个g(n)和两段零头,零头递归到底后枚举一下就行了。
3、
题解:
迷宫形状很有意思:(2k)^2的正方形,这说明该迷宫一定可以从中心“画十字”的方法划分成小正方形,其余的小正方形又可通过相同的方法分下去。在一个“田”字形中,可在十字的中心放一个L形,L形的缺口指向田字形中有公主的一部分。
一图见真相:
4、平面最近点对
(注:欧几里得距离即直线距离)
题解:
不妨按照x坐标排序。对于区间[l,r],我们将其分成mid左右两个部分。
两个点都在左侧:子问题Work(l,mid)
两个点都在右侧:子问题Work(mid+1,r)
两个点一个在左侧,一个在右侧:
重点考虑第三种情况 :
设δ为目前的ans,将范围剖分成6个长δ/2,宽(2/3)δ.
(下节预告:数据结构大显神通)