递归与回溯法
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Procedure Work(T,X,Y:Integer); {搜索路径,T为国都名的字符位置,X,Y为当前搜索的坐标} Var I : Integer; Begin If T=Length(S)+1 Then begin {搜索完,打印输出} Out; exit end; For I:=1 To 8 Do {八个方向进行搜索} Begin X:=X+Fx[I,1]; Y:=Y+Fx[I,2]; {坐标变化} If (A[X,Y]=S[T])And(B[X,Y]) Then Begin W:=W+Chr(I+48); {记录路径} B[X,Y]:=False; {设置已经搜索} Work(T+1,X,Y); {继续搜索下一个} Delete(W,Length(W),1);{恢复原路径} B[X,Y]:=True; {恢复标志} End; X:=X-Fx[I,1]; Y:=Y-Fx[I,2]; {返回后,坐标恢复} End; End;
⑶搜索范围:在当前状态不满足边界条件的情况下,应如何设计算符值的 范围。换句话说,如何设定for语句中循环变量的初值和终值。 ⑷约束条件和最优性要求:当前扩展出一个子结点后应满足什么条件方可 继续递归下去;如果是求满足某个特定条件的一条最佳路径,那么在扩展 出某个子状态后是否继续递归搜索下去,不仅取决于子状态是否满足约束 条件,而且还取决于子状态是否满足最优性要求。 ⑸参与递归运算的参数:将参与递归运算的参数设为递归子程序的值参或 局部变量。若这些参数的存储量大(例如数组)且初始值需由主程序传入, 为避免内存溢出,则必须将其设为全局变量,且回溯前需恢复其递归前的 值。
例如, 输入 100 3 40 80 50 100 70 150
输出 180 12
算法分析
本题可以用递归求解:设当前有N个物品,容量为M;因 为这些物品要么选,要么不选,我们假设选的第一个物 品编号为I(1~I-1号物品不选),问题又可以转化为有NI 个 物 品 ( 即 第 I+1~N 号 物 品 ) , 容 量 为 M-Wi 的 子 问 题……如此反复下去,然后在所有可行解中选一个效益 最大的便可。 另外,为了优化程序,我们定义一个函数如下: F[I]表示选第I~N个物品能得到的总效益。不难推出: F[N]=Pn F[I]=F[I+1]+Pi (I=1…N-1) 假设当前已选到第I号物品,如果当前搜索的效益值 +F[I+1]的值仍然比当前的最优值小,则没有必要继续搜 索下去。
procedure run(当前状态); var i:integer; begin if 当前状态为边界 then begin if 当前状态为最佳目标状态 then 记下最优结果; exit;{回溯} end; for i←算符最小值 to 算符最大值 do begin 算符i作用于当前状态,扩展出一个子状态; if (子状态满足约束条件)and(子状态满足最优性要求)then run(子状态) end; end;
递归的概念与应用 递归过程是借助于一个递归工 Nhomakorabea栈来实现的
问题向一极推进,这一过程叫做递推;
问题逐一解决,最后回到原问题,这一过程 叫做回归。 递归的过程正是由递推和回归两个过程组成。
递归的概念与应用
用递归方法编写的问题解决程序具有结构清晰,可 读性强等优点,且递归算法的设计比非递归算法的 设计往往要容易一些,所以当问题本身是递归定义 的,或者问题所涉及到的数据结构是递归定义的, 或者是问题的解决方法是递归形式的时候,往往采 用递归算法来解决。
简单回溯法
N皇后问题 背包问题 寻找国都名 ……
回溯法
回溯法也是搜索算法中的一种控制策略,但与枚举法不同的是,它是从初始状态出 发,运用题目给出的条件、规则,按照深度优秀搜索的顺序扩展所有可能情况,从 中找出满足题意要求的解答。回溯法是求解特殊型计数题或较复杂的枚举题中使用 频率最高的一种算法。
procedure solve(at:integer);{递归扩展第at个字母} var ch:char; i:integer; begin if at=n+1 then {若产生了一个满足条件的字串,则输出} begin writeln(f,s); inc(tl);exit{回溯} end;{then} for ch←'a' to ed do {搜索每一个可填字母} begin s←s+ch;i←1;{检查当前字串是否符合条件} while (i<=at div 2) and (copy(s,length(s)-i+1,i)<>copy(s,length(s)-2*i+1,i)) do inc(i); if i>at div 2 then solve(at+1); {若当前字串符合条件,则递归扩展下一个字母} delete(s,length(s),1){恢复填前的字串} end{for} end;{solve}
end; end;
细节处理
怎样判断某列放置了皇后 A:array [1..MaxN] of Boolean; {竖线被控制标记} 怎样判断某对角线上放置了皇后 B:array [2..MaxN * 2] of Boolean; {左上到右下斜线被控制标记} C:array [1–MaxN..MaxN–1] of Boolean; {左下到右上斜线被控制标记}
在应用回溯法求所有路径的算法框架解题时,应考虑如下几个重要因素:
⑴定义状态:即如何描述问题求解过程中每一步的状况。为了精简程序, 增加可读性,我们一般将参与子结点扩展运算的变量组合成当前状态列入 值参,以便回溯时能恢复递归前的状态,重新计算下一条路径;
⑵边界条件:即在什么情况下程序不再递归下去。如果是求满足某个特定 条件的一条最佳路径,则当前状态到达边界时并非一定意味着此时就是最 佳目标状态。因此还须增加判别最优目标状态的条件;
算法思想
将字符矩阵读入到二维数组,然后对每一个国都名进行 搜索,首先需要在矩阵中找到国都名的第一个字符,然 后沿八个方向进行搜索。直到找到国都名为止。若在矩 阵中没有找到,则输出相应的信息。 在搜索过程时,类似八皇后问题,建立一个标志数组, 标识已经搜索过的方向,在对八个方向搜索时,可以建 立一个方向数组,使得程序更加简洁明了 Const Fx : Array[1..8,1..2] Of Shortint {定义八个方向} =((0,1),(0,-1),(1,0),(-1,0),(1,-1),(-1,1),(1,1),(-1,-1));
寻找国都名
给出一个矩阵及一些国都名:
okdublin alpgocev rasmusmb oslondon yiblglrc krzurich oaibxmuz tpqglamv dublin tokyo london rome bonn paris oslo lima
搜索的方向
要求从这个矩阵中找出这些国都名, 并输出它们的起始位置及方向。
搜索的本质
一、两种题型: 1.简明的数学模型揭示问题本质。对于这一类试题,我们 尽量用解析法求解。 2.对给定的问题建立数学模型,或即使有一定的数学模型, 但采用数学方法解决有一定困难。对于这一类试题,我们 只好用模拟或搜索求解。 二、搜索的本质: 搜索的本质就是逐步试探,在试探过程中找到问题的 三、搜索问题考察的范围 1.算法的实现能力 2.优化算法的能力
构造字串
生成长度为n的字串,其字符从26个英文 字母的前p(p≤26)个字母中选取,使得没有 相邻的子序列相等。例如p=3,n=5时 ‘a b c b a’满足条件 ‘a b c b c’不满足条件 输入:n,p 输出:所有满足条件的字串
分析
状态:待扩展的字母序号at。实际上字串s亦参与了递归 运算,但是由于该变量的存储量太大,因此我们将s设为 全局变量; 边界条件和目标状态:产生了一个满足条件的字串,即 at=n+1; 搜索范围:第at位置可填的字母集{'a'.. chr(ord('a')+p-1)}; 约束条件:当前字串没有相邻子串相等的情况 var n,p:integer;{字串长度和可选字母的个数} tl:longint; { 满足条件的字串数} ed:char; { 可选字母集中的最大字母} s:string; {满足条件的字串}
编写如下递归程序
var n:integer; t:longint; function fac(n:integer):longint; begin if n=0 then fac:=1 else fac:=n*fac(n-1) end; begin write(‘n=’);readln(n); t:=fac(n); writeln(‘n!=’,T); end.
算法框架
Procedure Search(I:Integer; J:Byte); {递归求解} Var K :Byte; Begin If Now+F[J]<=Ans Then Exit; {如果没有必要搜索下去} If Now>Ans Then Begin {修改最优解} Ans:=Now; Out:=Ou; End; For K:=J To N Do {选取物品} If W[K]<=I Then Begin Now:=Now+P[K]; Ou[K]:=True; Search(I-W[K],K+1); Now:=Now-P[K]; Ou[K]:=False; End; End;
N=3时的递归过程
这相当于从菜心“推到”外层。 递归算法的出 发点不放在初始条件上,放在求解的目标上,从所 求的未知项出发逐次调用本身的求解过程,直到递 归的边界(即初始条件)。就本例而言,读者会认 为递归算法可能是多余的,费力而不讨好。但许多 实际问题不可能或不容易找到显而易见的递推关系, 这时递归算法就表现出了明显的优越性。下面我们 将会看到,递归算法比较符合人的思维方式,逻辑 性强,可将问题描述得简单扼要,具有良好的可读 性,易于理解. 许多看来相当复杂,或难以下手的 问题,如果能够使用递归算法就会使问题变得易于 处理。
算法基本框架
Procedure Try(I:integer); {搜索第I行皇后的位置} var j:integer; begin if I=n+1 then 输出方案; for j:=1 to n do if 皇后能放在第I行第J列的位置 then begin 放置第I个皇后;
对放置皇后的位置进行标记; Try(I+1) 对放置皇后的位置释放标记;
递归及其实现
递归算法在可计算性理论中占有重要地位,它是 算法设计的有力工具,对于拓展编程思路非常有用。 就递归算法而言并不涉及高深数学知识,只不过初 学者要建立起递归概念不十分容易。 我们先从一个最简单的例子导入。 用递归算法求 n! 定义:函数 fact( n ) = n! fact( n-1 ) = ( n-1 )! 则有 fact( n ) = n fact( n-1 ) 已知 fact( 1 ) = 1
0,1背包问题
已知一个容量大小为M重量的背包和N种物品,每种物品的重量为Wi。 若将物品放入背包将得到Pi的效益,求怎样选取物品将得到效益最大。 输入 从文件的第一行读入M和N,接下来N行,第i+1行的两个数分别表示第i 种物品的重量和效益。 输出 第一行输出一个数,表示背包所装物品的最大效益。 第二行输出若干个数字,分别表示所装物品的标号。
递归的概念与应用用递归方法编写的问题解决程序具有结构清晰可读性强等优点且递归算法的设计比非递归算法的设计往往要容易一些所以当问题本身是递归定义的或者问题所涉及到的数据结构是递归定义的或者是问题的解决方法是递归形式的时候往往采用递归算法来解决
递归与回溯教案
朱全民
递归
什么是递归?先看大家都熟悉的一个民间故事:从前有 座山,山上有座庙,庙里有一个老和尚在给小和尚讲故 事,故事里说,从前有座山,山上有座庙,庙里有一个 老和尚在给小和尚讲故事,故事里说……。象这样,一 个对象部分地由它自己组成,或者是按它自己定义,我 们称之为递归。 一个函数、过程、概念或数学结构,如果在其定义或说 明内部又直接或间接地出现有其本身的引用,则称它们 是递归的或者是递归定义的。在程序设计中,过程或函 数直接或者间接调用自己,就被称为递归调用。
N皇后问题
在N*N的棋盘上放置N个皇后而彼此不受攻击(即在棋 盘的任一行,任一列和任一对角线上不能放置2个皇 后),编程求解所有的摆放方法。
基本思想
由于皇后的摆放位置不能通过某种公式来确定, 因此对于每个皇后的摆放位置都要进行试探和纠 正,这就是“回溯”的思想。 在N个皇后未放置完成前,摆放第i个皇后和第 i+1个皇后的试探方法是相同的,因此完全可以 采用递归的方法来处理。