回溯算法的一些例题.docx
- 文档编号:10656603
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:11
- 大小:19.28KB
回溯算法的一些例题.docx
《回溯算法的一些例题.docx》由会员分享,可在线阅读,更多相关《回溯算法的一些例题.docx(11页珍藏版)》请在冰豆网上搜索。
回溯算法的一些例题
回溯算法
搜索与回溯是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。
回溯是搜索算法中的一种控制策略。
它的基本思想是:
为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。
如迷宫问题:
进入迷宫后,先随意选择一个前进方向,一步步向前试探前进,如果碰到死胡同,说明前进方向已无路可走,这时,首先看其它方向是否还有路可走,如果有路可走,则沿该方向再向前试探;如果已无路可走,则返回一步,再看其它方向是否还有路可走;如果有路可走,则沿该方向再向前试探。
按此原则不断搜索回溯再搜索,直到找到新的出路或从原路返回入口处无解为止。
递归回溯法算法框架[一]
procedureTry(k:
integer);
begin
fori:
=1to算符种数Do
if满足条件then
begin
保存结果
if到目的地then输出解
elseTry(k+1);
恢复:
保存结果之前的状态{回溯一步}
end;
end;
递归回溯法算法框架[二]
procedureTry(k:
integer);
begin
if到目的地then输出解
else
fori:
=1to算符种数Do
if满足条件then
begin
保存结果
Try(k+1);
end;
end;
例1:
素数环:
把从1到20这20个数摆成一个环,要求相邻的两个数的和是一个素数。
【算法分析】非常明显,这是一道回溯的题目。
从1开始,每个空位有20(19)种可能,只要填进去的数合法:
与前面的数不相同;与左边相邻的数的和是一个素数。
第20个数还要判断和第1个数的和是否素数。
〖算法流程〗1、数据初始化;2、递归填数:
判断第J种可能是否合法;
A、如果合法:
填数;判断是否到达目标(20个已填完):
是,打印结果;不是,递归填下一个;
B、如果不合法:
选择下一种可能;
【参考程序】
programz74;框架[一]
vara:
array[0..20]ofbyte;
b:
array[0..20]ofboolean;
total:
integer;
functionpd(x,y:
byte):
boolean;
vark,i:
byte;
begin
k:
=2;i:
=x+y;pd:
=false;
while(k<=trunc(sqrt(i)))and(imodk<>0)doinc(k);
ifk>trunc(sqrt(i))thenpd:
=true;
end;
procedureprint;
varj:
byte;
begin
inc(total);write('<',total,'>:
');
forj:
=1to20dowrite(a[j],'');
writeln;
end;
proceduretry(t:
byte);
vari:
byte;
begin
fori:
=1to20do
ifpd(a[t-1],i)andb[i]then
begin
a[t]:
=i;b[i]:
=false;
ift=20thenbeginifpd(a[20],a[1])thenprint;end
elsetry(t+1);
b[i]:
=true;
end;
end;
BEGIN
fillchar(b,sizeof(b),#1);
total:
=0;
try
(1);
write('total:
',total);
END.
通过观察,我们可以发现实现回溯算法的特性:
在解决过程中首先必须要先为问题定义一个解的空间.这个空间必须包含问题的一个解。
在搜索路的同时也就产生了新的解空间。
在搜索期间的任何时刻.仅保留从起始点到当前点的路径。
例2:
设有n个整数的集合{1,2,…,n},从中取出任意r个数进行排列(r 解法一: programit15;框架[一] typese=setof1..100; VARs: se;n,r,num: integer; b: array[1..100]ofinteger; PROCEDUREprint; vari: integer; begin num: =num+1; fori: =1tordo write(b[i]: 3); writeln; end; PROCEDUREtry(k: integer); VARi: integer; begin fori: =1tondo ifiinsthen begin b[k]: =i; s: =s-[i]; ifk=rthenprint elsetry(k+1); s: =s+[i]; end; end; BEGIN write('Inputn,r: ');readln(n,r); s: =[1..n];num: =0; try (1); writeln('number=',num); END. 解法二: programit15;框架[二] typese=setof1..100; VAR s: se; n,r,num,k: integer; b: array[1..100]ofinteger; PROCEDUREprint; vari: integer; begin num: =num+1; fori: =1tordo write(b[i]: 3); writeln; end; PROCEDUREtry(s: se;k: integer); VARi: integer; begin ifk>rthenprint else fori: =1tondo ifiinsthen begin b[k]: =i; try(s-[i],k+1); end; end; BEGIN write('Inputn,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+1 7=1+1+1+1+1+2 7=1+1+1+1+3 7=1+1+1+2+2 7=1+1+1+4 7=1+1+2+3 7=1+1+5 7=1+2+2+2 7=1+2+4 7=1+3+3 7=1+6 7=2+2+3 7=2+5 7=3+4 total=14 {参考程序} programjjj; vara: array[0..100]ofinteger;n,t,total: integer; procedureprint(t: integer); vari: integer; begin write(n,'='); fori: =1tot-1dowrite(a[i],'+'); writeln(a[t]); total: =total+1; end; proceduretry(s,t: integer); vari: integer; begin fori: =1tosdo if(a[t-1]<=i)and(i begin a[t]: =i; s: =s-a[t]; ifs=0thenprint(t) elsetry(s,t+1); s: =s+a[t]; end; end; begin readln(n); try(n,1); writeln('total=',total); readln; end. 例4、八皇后问题: 要在国际象棋棋盘中放八个皇后,使任意两个皇后都不能互相吃。 (提示: 皇后能吃同一行、同一列、同一对角线的任意棋子。 ) 放置第i个皇后的算法为: procedureTry(i); begin for第i个皇后的位置=1to8do; if安全then begin 放置第i个皇后; 对放置皇后的位置进行标记; ifi=8then输出 elseTry(i+1);{放置第i+1个皇后} 对放置皇后的位置释放标记,尝试下一个位置是否可行; end; end; 【算法分析】 显然问题的键在于如何判定某个皇后所在的行、列、斜线上是否有别的皇后;可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列上,则列号相同;如果同在/斜线上的行列值之和相同;如果同在\斜线上的行列值之差相同;如果斜线不分方向,则同一斜线上两皇后的行号之差的绝对值与列号之差的绝对值相同。 从下图可验证: 对于一组布局我们可以用一个一维数组来表示: A: ARRAY[1..8]OFINTEGER;A[I]的下标I表示第I个皇后在棋盘的第I行,A[I]的内容表示在第I行的第A[I]列,例如: A[3]=5就表示第3个皇后在第3行的第5列。 在这种方式下,要表示两个皇后I和J不在同一列或斜线上的条件可以描述为: A[I]<>A[J]ANDABS(I-J)<>ABS(A[I]-A[J]){I和J分别表示两个皇后的行号} 考虑每行有且仅有一个皇后,设一维数组A[1..8]表示皇后的放置: 第i行皇后放在第j列,用A[i]=j来表示,即下标是行数,内容是列数。 判断皇后是否安全,即检查同一列、同一对角线是否已有皇后,建立标志数组b[1..8]控制同一列只能有一个皇后,若两皇后在同一对角线上,则其行列坐标之和或行列坐标之差相等,故亦可建立标志数组c[1..16]、d[-7..7]控制同一对角线上只能有一个皇后。 从分析中,我们不难看出,搜索前进过程实际上是不断递归调用的过程,当递归返回时 即为回溯的过程。 programex1; vara: array[1..8]ofbyte; b: array[1..8]ofboolean; c: array[1..16]ofboolean; d: array[-7..7]ofboolean; sum: byte; procedurepr; vari: byte; begin fori: =1to8dowrite(a[i]: 4); inc(sum);writeln('sum=',sum); end; proceduretry(t: byte); varj: byte; begin forj: =1to8do{每个皇后都有8种可能位置} ifb[j]andc[t+j]andd[t-j]then{寻找放置皇后的位置} begin{放置皇后,建立相应标志值} a[t]: =j;{摆放皇后} b[j]: =false;{宣布占领第j列} c[t+j]: =false;{占领两个对角线} d[t-j]: =false; ift=8thenpr{8个皇后都放置好,输出} elsetry(t+1);{继续递归放置下一个皇后} b[j]: =true;{递归返回即为回溯一步,当前皇后退出} c[t+j]: =true; d[t-j]: =true; end; end; BEGIN fillchar(b,sizeof(b),#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);(i<3,j<8) 2: (i,j)→(i+1,j+2);(i<4,j<7) 3: (i,j)→(i-1,j+2);(i>0,j<7) 4: (i,j)→(i-2,j+1);(i>1,j<8) 搜索策略: S1: A[1]: =(0,0); S2: 从A[1]出发,按移动规则依次选定某个方向,如果达到的是(4,8)则转向S3,否 则继续搜索下一个到达的顶点; S3: 打印路径。 programexam2; constx: array[1..4,1..2]ofinteger=((2,1),(1,2),(-1,2),(-2,1));{四种移动规则} vart: integer;{路径总数} a: array[1..9,1..2]ofinteger;{路径} procedureprint(ii: integer);{打印} vari: integer; begin inc(t);{路径总数} fori: =1toii-1do write(a[i,1],',',a[i,2],'-->'); writeln('4,8',t: 5); readln; end; proceduretry(i: integer);{搜索} varj: integer; begin forj: =1to4do if(a[i-1,1]+x[j,1]>=0)and(a[i-1,1]+x[j,1]<=4)and (a[i-1,2]+x[j,2]>=0)and(a[i-1,2]+x[j,2]<=8)then begin a[i,1]: =a[i-1,1]+x[j,1]; a[i,2]: =a[i-1,2]+x[j,2]; if(a[i,1]=4)and(a[i,2]=8)thenprint(i) elsetry(i+1);{搜索下一步} a[i,1]: =0;a[i,2]: =0 end; end; BEGIN{主程序} try (2); END. 【例6】设有一个连接n个地点①—⑥的道路网,找出从起点①出发到达终点⑥的一切 路径,要求在每条路径上任一地点最多只能通过一次。 【算法分析】 从①出发,下一点可到达②或③,可以分支。 具体步骤为: ⑴假定从起点出发数起第k个点Path[k],如果该点是终点n就打印一条路径; ⑵如果不是终点n,且前方点是未曾走过的点,则走到前方点,定(k+1)点为到达路径,转步骤⑴; (3)如果前方点已走过,就选另一分支点; (4)如果前方点已选完,就回溯一步,选另一分支点为出发点; (5)如果已回溯到起点,则结束。 为了表示各点的连通关系,建立如下的关系矩阵: 第一行表示与①相通点有②③,0是结束标志;以后各行依此类推。 集合b是为了检查不重复点。 ProgramExam68; constn=6; roadnet: array[1..n,1..n]of0..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)); varb: setof1..n; path: array[1..n]of1..n; p: byte; procedureprn(k: byte); vari: byte; begin inc(p);write(’<’,p: 2,’>’,’’: 4) write(path[1]: 2); forI: =2tokdo write(’--’,path[i]: 2); writeln end; proceduretry(k: byte); varj: byte; begin j: =1; repeat path[k]: =roadnet[path[k-1],j]; ifnot(path[k]inb)then begin b: =b+[path[k]]; ifpath[k]=nthenprn(k) elsetry(k+1); b: =b-[path[k]]; end; inc(j); untilroadnet[path[k-1],j]=0 end; BEGIN b: =[1];p=0;path[1]: =1; try (2); readln END.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 回溯 算法 一些 例题