c语言技巧之搜索剪枝 联系客服

发布时间 : 星期二 文章c语言技巧之搜索剪枝更新完毕开始阅读9db5bdbac77da26925c5b07d

搜索问题

计算机学院2006级师范班 程文华

搜索被称为“通用的解题法”,在算法和人工智能方面占有非常重要的低位,特别是在各类ACM程序设计比赛中非常常见,在题目中一般位于中间位置,作为中等难度的题目出现。因此我们需要着重掌握各类的搜索算法,才能面对各类即将到来的ACM大赛。“只要功夫深,铁棒磨成针”,“冰冻三尺,非一日之寒”,要能熟练的掌握和AC比赛中的题目,必须在熟练掌握各类搜索算法的基础上勤加练题,也是唯一的好方法。

本次讲解中,首先给出有关搜索的基本概念,然后针对各类专题,讲解具体的几个例题并加于分析(注:题目全部选自poj中的题目)。

一 概念介绍

状态(state) 问题在某一时刻的进展情况的数学描述。

算符(operator)/ 状态转移 问题从一种状态变换成另一种(或几种)状态的操作。

解答树 搜索的过程实际是在遍历一个图,它的结点就是所有的状态,有向边对应于算符,而一个可行解就是一条从起始节点出发到目标状态集中任意一个结点的路径。特个图称为状态空间(state space),这样的搜索称为 状态空间搜索(Single-Agent Search) ,得到的遍历树称为解答数。

基本搜索法:

枚举 枚举法的基本思想是根据提出的问题枚举所有可能状态,并用问题给定的条件检验哪些是需要的,哪些是不需要的。能使命题成立,即为其解。枚举算法的特点比较单纯,往往容易写出程序,也容易证明算法的正确性和分析算法的时间复杂度,可以解决一些很小的问题。它的缺点是效率特别低,往往很多题目不能用枚举方法,用了只会超时。所以它适应于容易找到状态并且状态较少的题目。没有信心确定其状态较少,请勿立即动手写程序!

深度优先搜索DFS(有时称为 回溯法) 遵循的搜索策略是尽可能深地搜索图,在深度优先搜索中,对于最新发现的顶点,如果它还有以此为起点而未探索到的边,就沿此边继续搜索下去。当结点V的所有边都已被探寻过时,搜索将回溯到发现结点V有那条边的始结点。这一过程一直进行到已发现从源结点可达的所有结点为止。如果还存在未被发现的结点,则选择另一个未发现的结点作为新的源结点重新上面的过程,直至所有的结点都搜索到。

宽度优先搜索 BFS 遵循的搜索策略是从源结点开始,把所有该结点的子结点都搜索完,在搜索每个结点的时候都把其子结点都放入一个队列的后面等待以后搜索,当把此层结点全部搜索完时,所有的下层结点也就进入了队列,继续这样的过程。当仍然没有找到解并且还有没有搜索到的结点时,以没有搜索的结点作为源结点继续上述的搜索过程,直至找到解。

剪枝 在搜索的过程中,在还没到达叶结点之前就可以判断以此结点为根的子树不可能包含可行解或者最优解,因此不需要扩展这颗树,就像拿一把剪刀把这颗子树剪去,称为剪枝。

还有一些搜索算法的概念没有给出 如分支限界搜索,迭代加深搜索,迭代加宽搜索,A*算法等。他们都各种有适应的范围,也是较为复杂的搜索。

专题1:深度优先搜索DFS

深度优先搜索有很多呈现形式,一般用递归解决,但也可循环等解决,不管用什么实现,其都会遵循深度优先思想。下面是递归实现的程序框架,供初始学习者参考:

Procedure DepthFirstSearch(State:Statetype;depth:integer); Begin

For Operand:=1 to Operandcount(State) do Begin

NewState:=DoOperand(State,Operand); If Answer then PrintAnswer

Else if depth

说明:函数名为DepthFirstSearch。有两个形参State和depth,分别代表初始状态和搜索的层数。for语句从1开始循环所有能用的算符,循环体里意义是:初始状态上作用一种算符得到新的状态,随后就判断此状态是否为问题的解,如果是的话就输出之,不是的话向下递归,以新的状态作为初始状态,层数加1做为新的层数开始新的判断。这个框架只是最简单的,它有很多可以变形。另外,还有一些地方需要注意。 另给出一个C的深搜框架:(引自张邦佐老师的课件) void Backtrack(int t) { if(t>n) Output(x); else for(int i=f(n,t);i<=g(n,t);i++) { x[t]=h[i]; if ((Constraint(t)&& Bound(t)) Backtrack(t+1);

}

}

(1) 循环解决的深搜(此题又像枚举)

1166 The Clocks

Description

There are nine clocks in a 3*3 array (figure 1). The goal is to return all the dials to 12 o'clock with as few moves as possible. There are nine different allowed ways to turn the dials on the clocks. Each such way is called a move. Select for each move a number 1 to 9. That number will turn the dials 90' (degrees) clockwise on those clocks which are affected according to figure 2 below.

Input

Your program is to read from standard input. Nine numbers give the start positions of the dials. 0=12 o'clock, 1=3 o'clock, 2=6 o'clock, 3=9 o'clock.

Output

Your program is to write to standard output. Output a shortest sorted sequence of moves (numbers), which returns all the dials to 12 o'clock. You are convinced that the answer is unique.

Sample Input

3 3 0 2 2 2 2 1 2

Sample Output

4 5 8 9

大概题意:

有九个时钟,每个时钟只有4种状态:12:00,3:00,6:00,9:00。分别用0,1,2,3代替。现在有9种操作,如表2所示,每种操作能使对应的钟顺时针旋转90度。 现在给出9个时钟的初始状态,问怎么用最少的操作使得这9个时钟都指向12:00。 输入给出9个数字,用1,2,3,4代表每个时钟的初始状态。输出要求给出操作代号,此操作代号按升序排列。