资源描述
2019-2020年高中信息技术 全国青少年奥林匹克联赛教案 搜索法一在这里介绍两种基本的搜索算法:深度优先搜索和广度优先搜索法,以树的搜索为例,深度优先搜索法是优先扩展尚未扩展的且具有最大深度的结点;广度优先搜索法是在扩展完第K层的结点以后才扩展K+1层的结点。深度优先搜索法与前面讲的回溯法差不多,主要的区别是回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树,搜索树起记录解路径和状态判重的作用。为了减少存储空间,在深度优先搜索中,用标志的方法记录访问过的状态,这种处理方法使得深度优先搜索法与回溯法没什么区别了。在回溯法中,我们己分析了非递归的实现过程,在这里就只讨论深度优先的递归实现方法。深度优先搜索的递归实现过程:procedure dfs(i); for i:=1 to r do if 子结点mr符合条件then 产生的子结点mr入栈; if 子结点 mr 是目标结点 then 输出 else dfs(i+1);栈顶元素出栈(即删去mr);endif;endfor;在讲解递推法时,我们讨论了用递推法解骑土游历问题,在这里我们再看看如何用深度优先搜索法求解此题。搜索算法应用例1骑士游历:设有一个n*m的棋盘,在棋盘上任一点有一个中国象棋马,马走的规则为:1.马走日字 2.马只能向右走。当N,M 输入之后,找出一条从左下角到右上角的路径。例如:输入 N=4,M=4,输出:路径的格式:(1,1)-(2,3)-(4,4),若不存在路径,则输出no算法分析:我们以44的棋盘为例进行分析,用树形结构表示马走的所有过程(如下图),求从起点到终点的路径,实际上就是从根结点开始深度优先搜索这棵树。马从(1,1)开始,按深度优先搜索法,走一步到达(2,3),判断是否到达终点,若没有,则继续往前走,再走一步到达(4,4),然后判断是否到达终点,若到达则退出,搜索过程结束。为了减少搜索次数,在马走的过程中,判断下一步所走的位置是否在棋盘上,如果不在棋盘上,则另选一条路径再走。程序如下:const dx:array1.4of integer=(2,2,1,1); dy:array1.4of integer=(1,-1,2,-2);type map=record x,y:integer; end;var i,n,m:integer; a:array0.50of map;procedure dfs(i:integer);var j:integer;begin for j:=1 to 4 doif (ai-1.x+dxj0)and(ai-1.x+dxj0)and(ai-1.y+dyj(,aj.x,aj.y,); halt;输出结果并退出程序 end; dfs(i+1);搜索下一步 ai.x:=0;ai.y:=0;出栈 end;end;begin a1.x:=1;a1.y:=1; readln(n,m); dfs(2); writeln(no);end.从上面的例子我们可以看出,深度优先搜索算法有两个特点:1、 己产生的结点按深度排序,深度大的结点先得到扩展,即先产生它的子结点。2、 深度大的结点是后产生的,但先得到扩展,即“后产生先扩展”,与栈的工作原理相同,因此用堆栈作为该算法的主要数据结构,存储产生的结点。对于不同的问题,深度优先搜索算法基本上是一样的,但在具体处理方法和编程技巧上又都不相同,甚至会有很大的差别。我们再看看另一个例子。题二 选数(存盘名:NOIPxxpj)问题描述:已知 n 个整数 x1,x2,xn,以及一个整数 k(kn)。从n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:3712=2237192971219383121934。现在,要求你计算出和为素数共有多少种。例如上例,只有一种的和为素数:371929。 输入:键盘输入,格式为:n , k (1=n=20,kn)x1,x2,,xn (1=xi=5000000)输出:屏幕输出,格式为:一个整数(满足条件的种数)。 输入输出样例:输入:4 3 3 7 12 19输出:1算法分析:本题是求从n个数中选k个数的组合,并使其和为素数。求解此题时,先用深度优先搜索法生成k个数的组合,再判断k个数的和是否为素数,若为素数则总数加1。在程序实现过程中,用数组a存放输入的n个数,用s表示k个数的和,ans表示和为素数的个数。为了避免不必要的搜索,程序对搜索过程进行了优化,限制搜索范围,在搜索过程dfs(i,m)中,参数m为第i个数的上限,下限为n-k+i。源程序:var n,k,i: byte; ans,s:longint; a: array1 . 20 of Longint;procedure prime(s:longint);判断K个数的和是否为素数var i:integer;begin i:=2; while (sqr(i)=s)and(s mod i0) do inc(i); if sqr(i)s then inc(ans)若为素数则总数加1end;procedure dfs(i,m:byte);搜索第i个数, var j:byte;j表示第i个数的位置begin for j:=m to n-k+i do枚举第i个数 begin inc(s,aj);入栈 if i=k then prime(s) else dfs(i+1,j+1);继续搜第i+1个数 dec(s,aj)出栈 endend;begin readln(n,k); for i:=1 to n do read(ai); ans:=0; s:=0; dfs(1,1); writeln(ans);end.从上面的两个例子我们可以看出,用递归实现深度优先搜索比非递归更加方便。在使用深度搜索法解题时,搜索的效率并不高,所以要重视对算法的优化,尽可能的减少搜索范围,提高程序的速度。在下一篇中将继续介绍另一种搜索方法广度优先搜索法。
展开阅读全文