回溯搜索知识

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

i,j,k:integer; x:array[1..n] of integer;
{保存第i个皇后的列号} function place(k:integer):boolean; var i:integer; begin place:=true; for i:=1 to k-1 do if(x[i]=x[k])or(abs(x[i]-x[k])=abs(i-k)) then place:=false; end; procedure print; var i:integer; begin for i:=1 to n do write(x[i]:4); writeln; end;
例1、四皇后问题 (演示) 在4×4方格的棋盘内,放置四个皇后,使得任意两个皇后 不在同一行、同一列、同一条对角线上。请找出所有的摆法。 分析: 如果我们把4*4的棋盘看成是一个平面直角坐标系,那 么任意两个皇后在平面上的坐标应同时满足以下三个条件: ⑴两个皇后的横坐标(行号)不相等。 I≠K ⑵两个皇后的纵坐标(列号)不相等。 X[I] ≠ X[K] ⑶两个皇后的横坐标之差的绝对值不等于纵坐标之差的 绝对值。 |I-K|≠|X[I]-X[K]| 我们用数组x[i]来描述四个皇后在棋盘上的状态, x[i] =j表示在第i行的第j列放置了一个皇后。
k:=k+1 );
x[k]:=0
end end ; end. 1 2 3 0 4 1 3 4 2 0 0 4 3 1 2 2 0 1
k
例2、数字排列问题
列出所有从数字1到数字n的连续自然数的排列,要求所产生的 任一数字序列中不能出现重复的数字. 输入:n(1<=n<=9) 输出:由1~n组成的所有不重复的数字序列,每行一个序列. 样例 输入: 3 输出: 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
while(x[k]<=n)and( not place(k) ) do
x:array[1..n] of integer;{栈} function place(k:integer):boolean; var i:integer; begin place:=true; for i:=1 to k-1 do if ( x[i]=x[k])or (abs(x[i]-x[k])=abs(i-k))
所谓剪枝就是通过某种判断条件避免一些不必要的遍历过程形象地说就是剪去了搜索树中的某些枝b10c20d30e35f40g50h35i25j30当叶子结点d已找到了一个值为30的最短路径这时在搜索到g50h35j30时其路径长度已大于或等于了当时最优值因此再搜索下去毫无意义其下的结点都可以剪除
回溯搜索
回溯是一种模拟人类思维过程的算法思想。它的基本方 法是:按照深度优先的顺序向下一层扩展结点;扩展到某一 层时,若已无法继续扩展且仍未找到解,则退回到父结点, 从父结点的下一个分支开始,按同样的策略继续扩展……, 直到找到问题的解或证明无解。
then place:=false ; end; procedure print; {输出一种方案} var i:integer; begin for i:=1 to n do write(x[i]:4); writeln; end;
x[k]:=x[k]+1;{尝试第k行下一列} if ( x[k]>n) then k:=k-1 {回溯} else if k=n then ( print ) else begin {摆放下一个皇后} (
var i,k,n:integer;{k是栈顶指针} x:array[1..9] of integer;{栈} function place(k:integer):boolean; var i:integer; begin place:=true; for i:=1 to k-1 do if ( x[i]=x[k] )then begin place:=false; break end ; end; procedure print; var i:integer; begin for i:=1 to n do write(x[i],' '); writeln; end;
如此,从区域1出发,依次类推,直至区域n涂好颜色为止(i>n)。
const n=8; var i,j,k,:integer;{I是栈顶指针} m:array[1..n,1..n] of 0..1;{图的邻接矩阵} s:array[1..n] of integer;{栈,存放各区域的颜色} begin for i:=1 to n do begin for j:=1 to n do read(m[I,j]);readln; end; ( s[1]:=1 ); {首先给第一个图形涂上红色} i:=2; j:=1;
非递归回溯法的一般形式
K:=1; x[k]:=0;
While k > 0 do x[k]:=x[k]+1; while(x[k]<=n) and(不满足条件) x[k]:=x[k]+1; Y x[k]>n Y k:=k-1; 输出一种方案 K=m k:=k+1; x[k]:=0; N N do
例3.骑士游历(98年复赛试题) 设有一个n*m的棋盘(2≤n≤17,2≤m≤17),如下图,在棋盘上左下角有 一个中国象棋马。 (8,4)
回溯法的递归算法框架:
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;
begin readln(n,m); k:=1; y[k]:=0; x[0,1]:=0; x[0,2]:=0;{起跳点} while k>0 do begin y[k]:=y[k]+1; while (y[k]<=4)and(nok(k) do y[k]:=y[k]+1; ) if y[k]>4 then k:=k-1 {回溯} else begin x[k,1]:=x[k-1,1]+x0[y[k]]; x[k,2]:=x[k-1,2]+y0[y[k]]; if( (x[k,1]=n)and(x[k,2]=m) ) then begin for i:=( 0 to k-1 )do write(x[i,1],',',x[i,2],'->'); writeln(n,',',m); ( halt ) end else begin ( k:=k+1 ; y[k]:=0; ) end; end; end; writeln('NO'); end.
在回溯算法中,无论搜索进行到哪一结点,都只需要保存根结点到当前结点之前 绿色方框表示搜索过程中生成的合法结点,红色方框表示尝试生成的非法结点. 的路径,而不需要保存其他分支,因此只需要一个线性表即可保存搜索的“历 程”.在向纵深方向扩展结点时,结点是按照访问顺序逐一处理的;在回溯时,结点 空棋盘 是按照访问顺序的反序被逐一舍弃的.因此可借助栈来处理回溯算法中的结点.
begin readln(n); k:=1; x[k]:=0; while k>0 do begin x[k]:=x[k]+1; while ( x[k]<=n ) and (not place(k)) do x[k]:=x[k]+1; if x[k]>n then( k:=k-1 ) else if( k=n )then print else begin ( k:=k+1 ); x[k]:=0 end end ; end.
end; for i:=( 1 to n )do begin ( x[k]:=i ); if place(k) then( try(k+1) ); end; end ; 摆放下一个皇后 begin try(1);{摆放第一个皇后} end.
i+1个皇后 的摆放过程 是相同的, 所以可以用 递归的方法.
1---
2---
11--
12--
13--
14--
21--
22--
23--
24--
131-
132-
133-
134-
141-
142-
143-
144-
241-
1421
1422
1423
1424- 2411 2412 2413
const n=4; var i,j,k:integer;{K是栈顶指针}
begin k:=1;{摆放第一个皇后} x[k]:=0;{保存第k个皇后的列号} while ( k>0 ) do{栈非空时} begin 不满足约束条件 x[k]:=x[k]+1;
例4.四色问题(96年初赛试题)
设有下列形状的图形:有8个区域,其编号为1,2,…,8,图中 各区域的相邻关系用0(不相邻)、1(相邻)表示,如以下图表所示。 问题要求:将下面图形的每一部分涂上红(1)、黄(2)、蓝(3)、 绿(4)四种颜色之一,要求相邻部分的颜色不同。
1 2 1 0 1 0 0 1 1 0 3 0 1 0 1 0 1 0 0 4 0 0 1 0 1 1 0 0 5 0 0 0 1 0 1 0 0 6 0 1 1 1 1 0 1 0 7 1 1 0 0 0 1 0 1 8 1 0 0 0 0 0 1 0 8 2 1 0 1 7 3 5 6 4
2
1 3
1
4
(0,0) 马 马走的规则为:①马走日字;②马只能向右走; 即如下图如示:
问题:当n,m输入之后,找出一条从左下角到右上角的路径。 若不存在路径,则输出“NO”.
样例 输入:4 3 输出: 0,0 -> 1,2 -> 3,1 ->4,3
const x0:array[1..4]of integer=(1,2,2,1); y0:array[1..4]of integer=(2,1,-1,-2); var m,n,k,i:integer;{k是栈顶指针} x:array[0..1000,1..2]of integer;{保存每一跳的位置} y:array[1..1000]of integer;{栈,保存每一跳的方向} function nok(k:integer):boolean;{会不会出界} var i,j:integer; begin i:=x[k-1,1]+x0[y[k]]; j:=x[k-1,2]+y0[y[k]]; if(i>n)or(j>m)or(j<0) then nok:=true{出界} else nok:=false; end;
for i:=1 to n do wr的递归实现
const n=4; var
皇后序号 procedure try(k:integer); var i:integer; 因为从第i begin if( k=n+1 ) then 个皇后到第 begin print; ( exit )
while i<=n do begin while (j<=4)and(i<=n) do begin k:=1; while( (k<i)and((s[k]<>j)or(r[I,k]<>1)) ) do k:=k+1; if k<i then( j:=j+1 ) else begin ( S[i]:=j ); i:=i+1; j:=1 end end; if j>4 then begin i:=i-1; ( j:=s[i]+1 ) ; end; end;
1 2 3 4 5 6 7 8
0 1 0 0 0
1
分析:设m为图的邻接矩阵。按照涂色的先后顺序将方案存入堆栈s 中,其中s[k]为区域k的颜色码,区域k为第k个涂色的区域。在计算过程 中,我们必须记住当前涂色的区域号i,该区域称之为栈顶,区域准备涂颜 色j(1≤j≤4)。
首先将区域1涂红色(s[1]:=1),并准备涂区域2(i:=2),从颜色1 开始试探(j:=1)。区域 i 能否涂颜色j,关键取决于区域 i 的周边是否有 同色区域。搜索已涂色的区域1~ 区域i-1中与区域 i 相邻的区域k,看一看 这些区域是否涂了颜色j。若与区域 i 相邻的区域 k已经涂了颜色j,按照颜 色码递增顺序换一种颜色(j:=j+1);若区域 i 的周边没有涂颜色 j 的区域 ,则区域 i 可以涂颜色j(s[i]:=j)。“入栈”,准备涂区域i+1,也从颜色1 开始试探(i:=i+1,j:=1)。 对区域i,按照颜色码递增顺序试探颜色。如果区域 i 找不到合适的颜 色(j>4)则应该出栈,按照颜色码递增顺序调整区域i-1的颜色(i:=i-1, j:=s[i]+1)。
相关文档
最新文档