搜索及应用Word下载.docx
《搜索及应用Word下载.docx》由会员分享,可在线阅读,更多相关《搜索及应用Word下载.docx(8页珍藏版)》请在冰豆网上搜索。
![搜索及应用Word下载.docx](https://file1.bdocx.com/fileroot1/2022-10/13/0d3a13cb-2285-4d3d-99d8-eaadeb521681/0d3a13cb-2285-4d3d-99d8-eaadeb5216811.gif)
1、枚举法
枚举法的基本思想:
根据实际问题设计多重循环,一一枚举所有可能的状态,并用问题给定的约束条件检验哪些状态是需要的,哪些状态是不需要的。
能使命题成立的状态,即为其解。
虽然枚举法本质上属于搜索策略,但是适用枚举法求解的问题必须满足两个条件:
(1)可预先确定每个状态的元素个数n;
(2)可预先确定每个状态元素a1,a2,...,an的值域。
设ai1为状态元素ai的最小值;
aik为状态元素ai的最大值(1≤i≤n),即状态元素a1,a2,...,an的值域分别为a11≤a1≤a1k,a21≤a2≤a2k,ai1≤ai≤aik,...,an1≤an≤ank。
fora1:
=a11toa1kdo
fora2:
=a21toa2kdo
......
forai:
=ai1toaikdo
foran:
=an1toankdo
if状态(a1,a2,...,ai,...,an)满足检验条件then输出问题的解;
枚举法的优点:
由于枚举算法一般是现实问题的“直译”,且是建立在考察大量状态、甚至是穷举所有状态的基础之上的,因此比较直观,易于理解,其算法的正确性也比较容易证明。
枚举法的缺点:
枚举算法的效率取决于枚举状态的数量以及单个状态枚举的代价,因此效率比较低。
由于枚举法属于一种“直译”的解题方法,因此需要编程者注意认真审题,不要疏漏任何信息,根据题意以及蕴涵的规律准确地设定枚举对象、范围和约束条件。
【例2-1】猫老大数(TOJ1081,猫老大与彩虹的竞赛)
猫老大很喜欢研究数字,特别是喜欢质数。
一天,猫老大发现有一些数字可以表示成两个质数相乘的形式。
比如,10=2×
5.2,5都是质数,所以10是一个“猫老大数”。
所以猫老大决定考考彩虹,他告诉彩虹一个数n,判断n是不是“猫老大数”?
(1<
=n<
=231-1)
由于题目中含有条件1<
=231-1=maxlongint(LONG_MAX),所以我们可以先计算出1到的所有质数,共有4792个。
我们把这些质数存在数组中,完成了第一步的预处理工作。
有这样一个定理:
如果一个数是合数,那么它一定会有一个小于等于这个数的开方的不是1的因数。
根据这个理和题意,则如果这个数是合数,我们只须要枚举2到就可以找到一个质数。
然后根据唯一分解定理,我们可以得出猫老大数必定只能质数分拆为两个质数。
所以我们只须判断被一个质数除下后是不是质数即可。
由于两个质数是成反比例的(n一定),根据函数曲线,得出一种好的方法——先从小到大找到一个较小的,然后继续判断,直到找到第二个,判断剩下的是不是1。
【例2-2】仓库扩张(USACOContestDEC05)
在FJ的农场里有N(1<
=N<
=25000)个长方形仓库,这些仓库的四条边都分别与x轴、y轴平行,且四角坐标均为正数,范围为0..1000000。
这些仓库都不互相重叠,但是可能有一些点或者一段边是它们共有的。
一次,FJ得到了更多的奶牛去挤奶,因此他想扩张自己的仓库。
一个仓库有能够扩张的条件是它不与别的仓库存在任何一个公共角或者任意一段公共边。
请你计算有多少个仓库可以扩张。
这道题目枚举的方法有些特殊,需要一些枚举技巧。
首先在读入时,我们的处理方法是“拆边”,按横、纵两种拆。
拆开以后,我们应该把所有的横边排序,所有的纵边也排序。
横边排序规则:
1.按行排序。
2.行相等时按这一行的头坐标(线段左边端点纵坐标)排序。
3.如果再相等就按线段右边端点纵坐标排序。
这样就能够保证边的有序性,为下面做好铺垫。
竖边类同。
然后就是把所有的横边、纵边进行重叠处理。
我们可以把它们分开处理。
如果重叠(或临接),则两个仓库都不能扩张。
经过上述处理,我们可以发现我们需要比较的边数已经大大减少。
因此我们可以加上一些细节处理来加快速度。
2、深度优先搜索与回溯法
深度优先搜索所遵循的搜索策略是尽可能“深”地搜索树。
它的基本思想是:
为了求得问题的解,先选择某一种可能情况向前(子结点)探索,在探索过程中,一旦发现原来的选择不符合要求,就回溯至父亲结点重新选择另一结点,继续向前探索,如此反复进行,直至求得最优解。
一般地,从纵深搜索的策略讲,深度优先搜索与回溯法的本质是一致的,只不过是深度优先搜索一般用于现成的显式图,而回溯法一般用于隐式图的拓展、转化与搜索。
深度优先搜索的实现方式可以采用递归或者栈来实现。
Program递归深度优先搜索程序;
Const…;
Type…;
Var…;
ProcedureTry(Depth:
Longint,…);
VarI:
Longint,…;
Begin
IfDepth>
目标深度ThenBegin输出方案;
ExitEnd;
ForI:
=Depth深度可能决策范围DoBegin
If决策I符合展开条件ThenBegin
记录决策I;
Try(Depth+1);
删除决策I;
End;
Begin{MainCodeStartsHere}
程序读入数据并初始化;
Try
(1);
程序终止化;
End.
Program模拟递归深度优先搜索程序;
Var
Stack:
Array[1..MaxSize]ofStatetype;
Depth:
Longint;
…
程序读入数据并初始化;
=0;
Repeat
If可以展开ThenBegin
Depth:
=Depth+1;
在Stack[Depth]中记录第一种决策;
IfDepth=目标深度Then输出方案;
EndElseIf可以选择新决策Then改写Stack[Depth]为新决策
ElseBegin{Backtracking}
消除当前深度的尝试对其他变量的影响;
Depth:
=Depth–1;
End;
UntilDepth=0;
程序终止化;
【例2-3】谷仓安全保护(USACOContestNOV05)
FJ给谷仓安装了一个新的安全系统,并且要给牛群中的每一个奶牛安排一个有效的密码。
一个有效的密码由L(3<
=L<
=15)个小写字母(来自传统的拉丁字母集'
a'
...'
z'
)组成,至少有一个元音('
'
e'
i'
o'
u'
),至少两个辅音(除去元音以外的音节),并且有按字母表顺序出现的字母(例如,'
abc'
是有效的,而'
bac'
不是)。
给定一个长度L和C个小写字母,写一个程序,打印出所有的长度为L、能由这些字母组成的有效密码。
密码必须按字母表顺序打印出来,一行一个。
这道题可以用比较基础的DFS回溯解决。
我们可以用一个字符串来当作答案数组(一个比较巧妙的方法),然后层层递归产生新的答案。
当深度到达L时,判断是否满足元、辅音的要求,再输出。
然后回溯,以避免重复运算浪费时间。
由于这道题比较简单,在此也不详细说明了。
3、宽度优先搜索(广度优先搜索)
类似树的按层遍历,其过程为:
首先访问初始点Vi,并将其标记为已访问过,接着访问Vi的所有未被访问过可到达的邻接点Vi1、Vi2……Vit,并均标记为已访问过,然后再按照Vi1、Vi2……Vit的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依此类推,直到图中所有和初始点Vi有路径相通的顶点都被访问过为止。
广度优先搜索(BreadthFirstSearch),是一种与深度优先搜索不同的搜索。
这种搜索方法的通性是一般出现在最少多少步的情况。
这种方法由于它搜索顺序的特殊,使广度优先搜索能够保证搜到的是最小的。
宽搜框架:
(1)初始状态入队。
(2)I:
=1。
(3)对队列首部的状态进行第I种操作,存储结果。
(4)检查结果是否出现过,若未出现过,则此结果进队,DATA记下此结果,OP记下I的值,PRE记下变换至此结果的元素(即当前队列队头元素)的位置(下标)。
(5)若结果即为所求,至步骤⑦
(6)若I=N,队列第一个元素出队,至步骤②;
否则,I:
=I+1,至步骤③。
(7)J:
=当前元素下标
(8)队列中第J个元素的OP或DATA进辅助栈
(9)若J≠1,J:
=队列中第J个元素的PRE,至步骤⑧。
(10)全部弹栈,按出栈顺序输出。
【例2-4】救援行动(TOJ1051ByAngelForYou)
Angel被传说中神秘的邪恶的Moligpy人抓住了!
他被关在一个迷宫中。
迷宫的长、宽不超过200。
迷宫中有不可以越过的墙以及监狱的看守。
Angel的朋友带了一些救援队来到了迷宫中。
他们的任务是:
接近Angel。
我们假设接近Angel就是到达Angel所在的位置。
假设移动需要1单位时间,杀死一个看守也需要1单位时间。
到达一个格子以后,如果该格子有看守,则一定要杀死(否则会死的很难看的……只见那个看守开了9倍狙镜……)。
交给你的任务是,最少要多少单位时间,才能到达Angel所在的地方?
(只能向上、下、左、右4个方向移动)
本题有两种方法:
由救援队出发去搜与由Angel出发倒找救援队(这变成谁救谁了?
似乎救援队被困^_^)。
这两种方法的好坏不能比较(完全看数据),只是由于数据的关系(传说有数据错误,有两支救援队),我们只能采用后者的方法。
我们可以首先根据数据建图,然后从Angel出发,向四个方向探索,然后建队列。
由于位置一共只有200*200=40000种,所以我们只需要有数组模拟链表,再建一个队列,然后由列表扩展。
应该说本题不难,但是请采用由Angel出发倒找救援队的方法才能AC。
(这个是不是给我们提供了反向BFS的一种思路呢?
)
4、参数搜索
参数搜索是解决最优解问题一种很常见的方法。
其本质就是对问题加入参数,先解决有参数的问题,再不断调整参数,最终求得最优解。
【例2-5】分石子问题
有N个石子,每个石子重量Qi;
按顺序将它们装进K个筐中;
求一种方案,使得最重的筐最轻。
分析:
本题乍一看很容易想到动态规划。
事实上的确可以用动态规划解决,