1、回溯算法的一些例题回溯算法搜索与回溯是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。回溯是搜索算法中的一种控制策略。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。如迷宫问题:进入迷宫后,先随意选择一个前进方向,一步步向前试探前进,如果碰到死胡同,说明前进方向已无路可走,这时,首先看其它方向是否还有路可走,如果有路可走,则沿该方向再向前试探;如果已无路可走,则返回一步,再看其它方向是否还有路可走;如果有路可走,则沿该方
2、向再向前试探。按此原则不断搜索回溯再搜索,直到找到新的出路或从原路返回入口处无解为止。递归回溯法算法框架一procedure Try(k:integer);beginfor i:=1 to 算符种数 Doif 满足条件 thenbegin保存结果if 到目的地 then 输出解else Try(k+1);恢复:保存结果之前的状态回溯一步end;end;递归回溯法算法框架二procedure Try(k:integer);beginif 到目的地 then 输出解elsefor i:=1 to 算符种数 Doif 满足条件 thenbegin保存结果Try(k+1);end;end;例 1:素数
3、环:把从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数。【算法分析】 非常明显,这是一道回溯的题目。从1 开始,每个空位有 20(19)种可能,只要填进去的数合法:与前面的数不相同;与左边相邻的数的和是一个素数。第 20个数还要判断和第1个数的和是否素数。算法流程1、数据初始化; 2、递归填数:判断第J种可能是否合法;A、如果合法:填数;判断是否到达目标(20个已填完):是,打印结果;不是,递归填下一个;B、如果不合法:选择下一种可能;【参考程序】program z74;框架一var a:array0.20of byte;b:array0.20of boolean;total:i
4、nteger;function pd(x,y:byte):boolean;var k,i:byte;begink:=2; i:=x+y; pd:=false;while (k=trunc(sqrt(i)and(i mod k0) do inc(k);if ktrunc(sqrt(i) then pd:=true;end;procedure print;var j:byte;begininc(total);write(:);for j:=1 to 20 do write(aj, );writeln;end;procedure try(t:byte);var i:byte;beginfor i:=
5、1 to 20 doif pd(at-1,i)and bi thenbeginat:=i; bi:=false;if t=20 then begin if pd(a20,a1) then print;endelse try(t+1);bi:=true;end;end;BEGINfillchar(b,sizeof(b),#1);total:=0;try(1);write(total:,total);END.通过观察,我们可以发现实现回溯算法的特性:在解决过程中首先必须要先为问题定义一个解的空间这个空间必须包含问题的一个解。在搜索路的同时也就产生了新的解空间。在搜索期间的任何时刻仅保留从起始点到当
6、前点的路径。例 2:设有 n 个整数的集合1,2,n,从中取出任意 r 个数进行排列(rr then printelsefor i:=1 to n doif i in s thenbeginbk:=i;try(s-i,k+1);end;end;BEGINwrite(Input n,r:);readln(n,r);s:=1.n;num:=0;try(s,1);writeln(number=,num);readln;END.例3、任何一个大于1的自然数n,总可以拆分成若干个小于n 的自然数之和.当n=7共14种拆分方法:7=1+1+1+1+1+1+17=1+1+1+1+1+27=1+1+1+1+3
7、7=1+1+1+2+27=1+1+1+47=1+1+2+37=1+1+57=1+2+2+27=1+2+47=1+3+37=1+67=2+2+37=2+57=3+4total=14参考程序program jjj;var a:array0.100of integer;n,t,total:integer;procedure print(t:integer);var i:integer;beginwrite(n,=);for i:=1 to t-1 do write(ai,+);writeln(at);total:=total+1;end;procedure try(s,t:integer);var
8、i:integer;beginfor i:=1 to s doif (at-1=i)and(in) thenbeginat:=i;s:=s-at;if s=0 then print(t)else try(s,t+1);s:=s+at;end;end;beginreadln(n);try(n,1);writeln(total=,total);readln;end.例 4、八皇后问题:要在国际象棋棋盘中放八个皇后,使任意两个皇后都不能互相吃。(提示:皇后能吃同一行、同一列、同一对角线的任意棋子。)放置第i个皇后的算法为:procedure Try(i);beginfor 第i 个皇后的位置=1 t
9、o 8 do;if 安全 thenbegin放置第 i个皇后;对放置皇后的位置进行标记;if i=8 then 输出else Try(i+1);放置第 i+1个皇后对放置皇后的位置释放标记,尝试下一个位置是否可行;end;end;【算法分析】显然问题的键在于如何判定某个皇后所在的行、列、斜线上是否有别的皇后;可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列上,则列号相同;如果同在斜线上的行列值之和相同;如果同在斜线上的行列值之差相同;如果斜线不分方向,则同一斜线上两皇后的行号之差的绝对值与列号之差的绝对值相同。从下图可验证:对于一组布局我们可以用一个一维数组来表示:A:ARR
10、AY 1.8 OF INTEGER;AI的下标I表示第I个皇后在棋盘的第I行,AI的内容表示在第 I行的第 AI列,例如:A3=5就表示第3个皇后在第3行的第5列。在这种方式下,要表示两个皇后 I和 J不在同一列或斜线上的条件可以描述为:AIAJ AND ABS(I-J)ABS(AI-AJ)I和 J分别表示两个皇后的行号考虑每行有且仅有一个皇后,设一维数组A1.8表示皇后的放置:第i行皇后放在第j列,用Aij来表示,即下标是行数,内容是列数。判断皇后是否安全,即检查同一列、同一对角线是否已有皇后,建立标志数组b1.8控制同一列只能有一个皇后,若两皇后在同一对角线上,则其行列坐标之和或行列坐标之
11、差相等,故亦可建立标志数组c1.16、d-7.7控制同一对角线上只能有一个皇后。从分析中,我们不难看出,搜索前进过程实际上是不断递归调用的过程,当递归返回时即为回溯的过程。program ex1;var a:array1.8 of byte;b:array1.8 of boolean;c:array1.16 of boolean;d:array-7.7 of boolean;sum:byte;procedure pr;var i:byte;beginfor i:=1 to 8 do write(ai:4);inc(sum);writeln( sum=,sum);end;procedure tr
12、y(t:byte);var j:byte;beginfor j:=1 to 8 do每个皇后都有8种可能位置if bj and ct+j and dt-j then 寻找放置皇后的位置begin 放置皇后,建立相应标志值at:=j;摆放皇后bj:=false;宣布占领第j列ct+j:=false;占领两个对角线dt-j:=false;if t=8 then pr 8个皇后都放置好,输出else try(t+1);继续递归放置下一个皇后bj:=true; 递归返回即为回溯一步,当前皇后退出ct+j:=true;dt-j:=true;end;end;BEGINfillchar(b,sizeof(b
13、),#1);fillchar(c,sizeof(c),#1);fillchar(d,sizeof(d),#1);sum:=0;try(1);从第1个皇后开始放置END.例 5:马的遍历中国象棋半张棋盘如图 4(a)所示。马自左下角往右上角跳。今规定只许往右跳,不许往左跳。比如图 4(a)中所示为一种跳行路线,并将所经路线打印出来。打印格式为:0,0-2,1-3,3-1,4-3,5-2,7-4,8分析:如图4(b),马最多有四个方向,若原来的横坐标为j、纵坐标为i,则四个方向的移动可表示为:1: (i,j)(i+2,j+1); (i3,j8)2: (i,j)(i+1,j+2); (i4,j0,j
14、1,j);writeln(4,8,t:5);readln;end;procedure try(i:integer); 搜索var j:integer;beginfor j:=1 to 4 doif (ai-1,1+xj,1=0) and (ai-1,1+xj,1=0) and (ai-1,2+xj,2=8) thenbeginai,1:=ai-1,1+xj,1;ai,2:=ai-1,2+xj,2;if (ai,1=4) and (ai,2=8) then print(i)else try(i+1); 搜索下一步ai,1:=0;ai,2:=0end;end;BEGIN 主程序try(2);END
15、.【例 6】设有一个连接n个地点的道路网,找出从起点出发到达终点的一切路径,要求在每条路径上任一地点最多只能通过一次。【算法分析】从出发,下一点可到达或,可以分支。具体步骤为:假定从起点出发数起第 k 个点 Pathk,如果该点是终点n就打印一条路径;如果不是终点 n,且前方点是未曾走过的点,则走到前方点,定(k+1)点为到达路径,转步骤;(3)如果前方点已走过,就选另一分支点;(4)如果前方点已选完,就回溯一步,选另一分支点为出发点;(5)如果已回溯到起点,则结束。为了表示各点的连通关系,建立如下的关系矩阵:第一行表示与相通点有,0 是结束标志;以后各行依此类推。集合b是为了检查不重复点。P
16、rogram Exam68;const n=6;roadnet: array1.n, 1.n of 0.n=( (2,3,0,0,0,0),(1,3,4,0,0,0),(1,2,4,5,0,0),(2,3,5,6,0,0),(3,4,6,0,0,0),(4,5,0,0,0,0) );var b: set of 1.n;path: array1.n of 1.n;p: byte;procedure prn(k: byte);var i: byte;begininc(p); write(, :4)write (path1:2);for I:=2 to k dowrite (-, path i :2);writelnend;procedure try(k: byte);var j: byte;beginj:=1;repeatpathk:=roadnet path k-1, j ;if not (path k in b) thenbeginb:=b+path k ;if path k=n then prn (k)else try(k+1);b:=b-path k ;end;inc(j);until roadnet path k-1, j =0end;BEGINb:=1; p=0; path1:=1;try(2);readlnEND.
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1