算法第5章-第2讲-回溯算法2递归形式
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第2讲 回溯法
算法3递归回溯:
output( ) { t=t+1;//这里的t只是用来统计满足条件的解 的个数 print(t,' '); for( k=1;k<=n;k++) print(a[k],' '); print(“ 换行符”); }
第2讲 回溯法
递归回溯算法说明
递归算法的回溯是由函数调用结束自动完成的,也 不需要指出回溯点,(类似算法2中的k=k-1),但需要 “清理现场”——将当前点占用的位置释放,也就是 算法try( )中的后三个赋值语句。
a[xx,yy]=0;
//回溯,恢复未走标志 }
第2讲 回溯法
output( ) { count=count+1;//count用来记录满足约束的解个数 print(“换行符”); print('count=',count); for y=1 to n do {print(“换行符”); for x=1 to m do print(a[y,x]); } }
第2讲 回溯法
基本的回溯搜索 例2:马的遍历问题
在n×m的棋盘中,马只能走日字。马从位置(x,y) 处出发,把棋盘的每一格都走一次,且只走一次。找出 所有路径。
第2讲 回溯法
认识象棋 每步棋先横走或竖走一格,然后再斜走一格,没有蹩马 腿的限制,每次均走“日”字型
第2讲 回溯法
1、问题分析 马是在棋盘的点上行走的,所以这里的棋盘是指 行有N条边、列有M条边。而一个马在不出边界的情 况下有八个方向可以行走,如当前坐标为(x,y)则行 走后的坐标可以为: (x-1,y+2) (x+1,y+2) (x+1,y+2) , (x+1, y-2) (x+2,y+1) , (x+2, y-1) (x-1 ,y- 2) , (x -1, y+2) (x-2 ,y- 1) , (x -2, y+1)
1 6 2 7 3 8 4
第2讲 回溯法
try(int i)
{ int j; for(j=1;j<=n;j++) //j表示列号,第i个皇后有n种可能位置 if (b[j]=0) and (c[i+j]=0) and (d[i-j+n]=0)//判断位置是否冲突
{a[i]=j;
//第i行第j列可以摆放编号为i的皇后
x4=4 x4=3 5 7 10 12 15 17 21 23 26 28 31 33 37 39 42 44 47 49 53 55 58 60 63 65
四皇后问题的解空间树
4!=24 leaves
第2讲 回溯法
算法设计过程 确定问题的解空间 在应用回溯法解问题时,首先应明确定义问题的解空 间。问题的解空间应至少包含问题的一个解。 确定结点的扩展规则 如:每个皇后在一行中的不同位置移动,而象棋中 的马只能走“日”字等。 搜索解空间 回溯算法从开始节点(根节点)出发,以深度优先的方 式搜索整个解空间。
1 6 2 7 3 8 4
第2讲 回溯法
递归回溯算法
int a[20],b[20],c[40],d[40]; int n,t,i,j,k; //t记录解的个数,i控制行,j控制列 main( ) 2 5 { int i, 3 input(n); //输入皇后的个数 6 4 初 for(i=1;i<=n;i++) 7 { b[i]=0;//记录棋盘n个列 始 5 c[i+1]=0; c[n+i]=0;//记录棋盘负对角线 化 d[i]=0; d[n+i-1]=0;//记录棋盘主对角线 b[1] b[2] b[3] b[4] } try(1); }
第2讲 回溯法
xi表示第i个皇后所在位置的列号 1 34 1 35 50
x1=1
2 x2=2 3 x3=3 18
3
8
4
13
1 19
3 4 24 29
2
40
4
45
1 51
2 56
3 61
x3=4 4 6 9 11 14 16 20 22 25 27 30 32 36 38 41 43 46 48 52 54 57 59 62 64
第 2讲
回溯算法思想与框架
回溯法
回溯法基本思想 算法设计过程 算法框架
第2讲 回溯法
回溯法基本思想
回溯法是在包含问题的所有解的解空间树(或森林) 中.按照深度优先的策略,从根结点出发搜索解空间 树.算法搜索至解空间树的任一结点时,总是先判断 该结点是否满足问题的约束条件。如果满足则进入 该子树,继续按深度优先的策略进行搜索.否则,不 去搜索以该结点为根的子树,而是逐层向其祖先结 点回溯。 回溯法就是对隐式图的深度优先搜索算法.(隐式图 指问题的解空间多是树形或图形结构,并没有显式 给出)
第五章
每节一经典
回溯法
掉头!退回到前一步
第2讲 回溯法
算法设计3:递归算法 对于回溯算法,更方便的是用递归控制方式实现,这 种方式也可以解决任意的n皇后问题,算法的思想同样用 深度优先搜索,在不满足约束条件时及时回溯。 这里,我们 “利用数组记录状态信息”的技巧,用三 个数组c,b,d分别记录棋盘上的n个列、2n-1个主对角线和 2n-1个负对角线的占用情况。
第2讲 回溯法
3、数据结构设计 1)用一个变量dep记录递归深度,也就是走过的
点数,当dep=n×m时,找到一组解。
2)用n×m的二维数组记录马行走的过程,初始 值为0表示未经过。搜索完毕后,起点存储的是“1” ,终点存储的是 “n×m”。 例如:a[1,1]=1
4、算 法
第2讲 回溯法
int n=5 , m=4, dep , i, x , y , count; int fx[8]={1,2,2,1,-1,-2,-2,-1} , (x,y)处y的8个方向 fy[8]={2,1,-1,-2,-2,-1,1,2} , a[n,m];//点被马经过与否的标记,未:a[xx][yy]=0 初 否则: a[xx][yy]=dep //当前所走步数 始 main( ) 化 {count=0; dep=1; print('input x,y'); input(x,y); if (y>n or x>m or x<1 or y<1) { print('x,y error!'); return;} for(i=1;i<=;i++) for(j=1;j<=;j++) a[i][j]=0; a[x][y]=1; find(x,y,2); //2为深度dep if (count=0 ) print(“No answer!”); else print(“count=!”,count); }
b[j]=1; //占领第j列 c[i+j]=1; d[i-j+n]=1; //占领两个对角线 if (i<n) try(i+1); //n个皇后没有摆完, 递归摆放下一皇后 else
5
6 7
2 3
4 5
1 6 2 7 3 8 4
output( ); //完成任务,打印结果 b[1] b[2] b[3] b[4] b[j]=0; c[i+j]=0; d[i-j+n]=0; //回溯,清理现场,从低向上回溯 } }
第2讲 回溯法
排列及排列树的回溯搜索 例3: 输出自然数1到n所有不重复的排列,即n的全
排列
如:n=3则n的全排列为: 123 132 213 231 312 321
第2讲 回溯法
1、算法设计 n的全排列是一组n元一维向量:(x1,x2,x3,……,xn),搜索 空间是:1≤xi≤n,i=1,2, 3,……n,约束条件很简单,xi互不相同 。 这里我们采用第三章“3.2.3利用数组记录状态信息 ”的技巧,设置n个元素的数组d,其中的n个元素用来记 录数据1--n的使用情况,已使用置1,未使用置0.
第2讲 回溯法
1 x1=1 2 18 4 1 19 3 4 24 29 34 50
活节点(扩展结点)
死结点 2 3
3 2
8
13
2 4
1
……
……
……
4 6 9 11 14 16 20 22 25 27 30 32
2
3
5 7 10 12 15 17 21 23 26 28 31 33 请同学们找出另一个合理解
第2讲 回溯法
find(int y,int x,int dep) { int i , xx , yy ; for i=1 to 8 do //加上方向增量,形成新的坐标 {xx=x+fx[i]; yy=y+fy[i]; if (check(xx,yy)=1) //判断新坐标是否出界,是否已走过? {a[xx,yy]=dep; //走向新的坐标 if (dep=n*m) output( ); else find(xx,yy,dep+1); } //从新坐标出发,递归下一层 }
非递归回溯框架
int a[n],i; 初始化数组a[ ]; i=1; While (i>0(有路可走))and ([未达到目标]) //还未回溯到头 { if (i>n) //搜索到叶节点 搜索到一个解,输出; else //正在处理第i个元素 { a[i]第一个可能的值; while (a[i]不满足约束条件 且 在搜索空间内) a[i]下一个可能的值; if (a[i]在搜索空间内) {标识占用的资源; i=i+1;} // 扩展下一个结点 else {清理所占的状态空间;i=i-1; } //回溯 }}
第2讲 回溯法
1、算法描述
• 算法描述 – 初始化空排列,开始逐位进行排列; – 循环,直到第一位出现回溯 – 寻找当前位的下一个可排列数字; – 若存在可排列数字 – 排列 – 若已排列满,则 – 输出一个排列,并删除该位(准备下一个排列) – 否则 – 准备排列下一位 – 退回上一位,并删除所排列数字
第2讲 回溯法
算法设计过程
搜索解空间 搜索开始的结点就成为一个活结点,同时也成为当 前的扩展结点。在当前的扩展结点处,以深度优先方式 移至一个新结点。这个新结点就成为一个新的活结点, 并成为当前扩展结点。如果在当前的扩展结点处不能再 向纵深方向移动,则当前扩展结点就成为死结点。此时 ,应往回移动至最近的一个活结点处,并使这个活结点 成为当前的扩展结点。回溯法即以这种工作方式递归wk.baidu.com 在解空间中搜索,直至找到所要求的解或解空间中已没 有活结点时为止。
(x-2,y+1) (x+2,y+1)
(x,y)
(x-2,y-1) (x+2,y-1) (x-1,y-2) (x+1,y-2)
2、算法设计
第2讲 回溯法
搜索空间是整个n×m个棋盘上的点。约束条件是不出 边界且每个点只经过一次。结点的扩展规则如问题分 析中所述。 搜索过程是从任一点(x,y)出发,按深度优先的原则, 从八个方向中尝试一个可以走的点,直到走过棋盘上 所有n×m个点。用递归算法易实现此过程。 注意问题要求找出全部可能的解,就要注意回溯过程 的清理现场工作,也就是置当前位置为未经过。
第2讲 回溯法
算法框架 问题框架
设问题的解是一个n维向量(a1,a2,……,an),约束条件
是ai(i=1,2,3……n),之间满足某种关系,记为f(ai) 例如:n皇后问题中,第i个皇后所在的位置和其它 皇后所在的位置,不能在同一行或者同一列,或者同一 对角线。
非递归回溯算法框架
第2讲 回溯法
第2讲 回溯法
递归算法框架
int a[n]; try(int i) {if (i>n) 输出结果; else for( j=下界 ; j<=上界; j++) //枚举i所有可能的路径 { if ( f(j) ) //满足限界函数和约束条件 { a[i]=j; …… //其它操作 try(i+ 1);} } 回溯前的清理工作(如a[i]置空值等); }} 一般情况下用递归函数来实现回溯法比较简单,其中i为搜索 深度。
第2讲 回溯法
以四阶棋盘为例,如图,当n=4时,共有2n-1=7个 主对角线,对应地也有7个负对角线。
n条
n-1条
四皇后棋盘示意图
第2讲 回溯法
用i,j分别表示皇后所在的行和列(即:i号皇后所在的列为j) ,同一主对角线上的行列下标的差一样.表达式i-j范围为-n+1~ n-1,为理解简便,我们对-n+1~n-1两边同时增加n, 则i-j变为ij+n,其取值范围为1~2n-1,用i-j+n对主对角线进行编号。同 样地,负对角线上行列下标的和一样,用表达式i+j编号,则范 围为2~2n。 5 6 7 2 3 4 5