N皇后问题 回溯法
n皇后问题解法总数 规律

n皇后问题解法总数规律n皇后问题是一个经典的数学问题,最早由欧洲的数学家在18世纪提出。
问题的描述是在一个n×n的棋盘上放置n个皇后,使得它们互不攻击,即任意两个皇后不能处于同一行、同一列或同一斜线上。
这个问题的解法总数一直是一个引人关注的问题,不同的n对应的解法总数有一定的规律。
首先,我们来分析一下较小的n对应的解法总数。
当n=1时,显然只有一种解法。
当n=2时,由于两个皇后不能处于同一行、同一列或同一斜线上,所以无解。
当n=3时,也无解。
当n=4时,解法总数为2。
具体的解法如下所示:. Q . . . . Q .. . . Q Q . . .Q . . . . . . Q. . Q . . Q . .可以发现,当n=4时,只有两种解法。
随着n的增大,解法总数逐渐增加。
当n=5时,解法总数为10。
当n=6时,解法总数为4。
当n=7时,解法总数为40。
当n=8时,解法总数为92。
当n=9时,解法总数为352。
可以看出,解法总数并不是一个简单的线性增长。
为了进一步分析n皇后问题解法总数的规律,我们可以使用回溯算法。
回溯算法是一种穷举搜索的算法,通过尝试所有可能的解,找到所有满足条件的解。
在n皇后问题中,我们可以从第一行开始,依次尝试每一列,如果当前位置可以放置一个皇后,我们就继续尝试下一行,直到放置了n个皇后,即找到了一种解法。
然后,我们回溯到上一行,尝试该行的下一列,继续寻找下一种解法。
当回溯到第一行时,即找到了所有的解法。
通过回溯算法,我们可以得到n=1到n=9的解法总数,如下所示:n=1,解法总数为1n=2,解法总数为0n=3,解法总数为0n=4,解法总数为2n=5,解法总数为10n=6,解法总数为4n=7,解法总数为40n=8,解法总数为92n=9,解法总数为352可以观察到,n=1到n=9的解法总数并不是一个简单的规律,解法总数的增长呈现出一定的波动。
然而,当n=10时,解法总数突然增加到724。
n皇后问题算法设计

算法设计及分析n皇后问题---回溯求解国际象棋中皇后威力很大,它可以象“车”一样沿直线上下或左右移动;也可以如同“象”那样沿着斜线移动。
双方的皇后是不能在同一行或同一列或同一斜线上对持的。
那么,在一张空白的国际象棋盘上最多可以放上几个皇后并且不让它们互相攻击呢?这个问题是伟大数学家高斯在十九世纪中期提出来的,并作了部分解答。
高斯在棋盘上放下了N个互不攻击的皇后,他还认为可能有N种不同的放法,这就是有名的“N皇后”问题。
如果你动手试试,就一定会发现开头几颗皇后很容易放置,越到后来就越困难。
由于我们的记忆有限,很可能在某个位置放过子后来证明不行取消了,但是以后又重新放上子去试探,这样就会不断地走弯路,花费大量的精力。
因此,必须找到一个简易有效、有条不紊的法则才行。
回溯法的基本思想:对于用回溯法求解的问题,首先要将问题进行适当的转化,得出状态空间树。
这棵树的每条完整路径都代表了一种解的可能。
通过深度优先搜索这棵树,枚举每种可能的解的情况;从而得出结果。
在深度优先搜索的过程中,不断的将每个解(并不一定是完整的,事实上这也就是构造约束函数的意义所在)与约束函数进行对照从而删除一些不可能的解,这样就不必继续把解的剩余部分列出从而节省部分时间。
不妨以8皇后为例,设8皇后为x i,她们分别在第i行(i=1,2,3,4,5,6,7,8),这样问题的解空间就是一个8个皇后所在列的序号,为n元一维向量(x1,x2,,x3,x4,x5,x6,x7,x8),搜索空间是1≤x i≤8(i=1,2,3,4,5,6,7,8),共88个状态。
约束条件是8个点(1,x1),(2,x2),(3,x3),(4,x4),(5,x5),(6,x6),(7,x7),(8,x8)不在同一列和同一对角线上。
虽然问题共有88个状态,但算法不会真正地搜索这么多的状态,因为回溯法采用的是“走不通就掉头”的策略,而形如(1,1,x3,x4,x5,x6,x7,x8)的状态共有86个,由于1,2号皇后在同一列不满足约束条件,回溯后这些状态是不会搜索的。
回溯法实验(n皇后问题)(迭代法)

算法分析与设计实验报告第三次附加实验附录:完整代码(回溯法)//回溯算法递归回溯n皇后问题#include<iostream>#include<time.h>#include<iomanip>#include"math.h"using namespace std;class Queen{friend int nQueen(int); //定义友元函数,可以访问私有数据private:bool Place(int k); //判断该位置是否可用的函数void Backtrack(int t); //定义回溯函数int n; //皇后个数int *x; //当前解long sum; //当前已找到的可行方案数};int main(){int m,n;for(int i=1;i<=1;i++){cout<<"请输入皇后的个数:"; //输入皇后个数cin>>n;cout<<"皇后问题的解为:"<<endl;clock_t start,end,over; //计算程序运行时间的算法start=clock();end=clock();over=end-start;start=clock();m=nQueen(n); //调用求解的函数cout<<n<<"皇后问题共有";cout<<m<<"个不同的解!"<<endl; //输出结果end=clock();printf("The time is %6.3f",(double)(end-start-over)/CLK_TCK); //显示运行时间cout<<endl;}system("pause");return 0;}bool Queen::Place(int k)//传入行号{for(int j=1;j<k;j++){if((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))//如果两个在同一斜线或者在同一列上,说明冲突,该位置不可用{return false;}}return true;}void Queen::Backtrack(int t){if(t>n){sum++;/*for(int i=1;i<=n;i++) //输出皇后排列的解{cout<<x[i]<<" ";}cout<<endl;*/}else{//回溯探索第i行的每一列是否有元素满足要求for(int i=1;i<=n;i++){x[t]=i;if(Place(t)){Backtrack(t+1);}}}}int nQueen(int n){Queen X; //定义Queen类的对象X//初始化XX.n=n;X.sum=0;int *p=new int[n+1]; //动态分配for(int i=0;i<=n;i++) //初始化数组{p[i]=0;}X.x=p;X.Backtrack(1);delete[] p;return X.sum;//输出解的个数}完整代码(回溯法)//回溯算法迭代回溯n皇后问题#include<iostream>#include<time.h>#include<iomanip>#include"math.h"using namespace std;class Queen{friend int nQueen(int); //定义友元函数private:bool Place(int k); //定义位置是否可用的判断函数void Backtrack(void); //定义回溯函数int n; // 皇后个数int *x; // 当前解long sum; // 当前已找到的可行方案数};int main(){int n,m;for(int i=1;i<=1;i++){cout<<"请输入皇后的个数:";cin>>n;cout<<n<<"皇后问题的解为:"<<endl;clock_t start,end,over; //计算程序运行时间的算法start=clock();end=clock();over=end-start;start=clock();m=nQueen(n); //调用求解皇后问题的函数cout<<n<<"皇后问题共有";cout<<m<<"个不同的解!"<<endl;end=clock();printf("The time is %6.3f",(double)(end-start-over)/CLK_TCK); //显示运行时间cout<<endl;}system("pause");return 0;}bool Queen::Place(int k){for (int j=1;j<k;j++){if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k])) //如果两个皇后在同一斜线或者在同一列上,说明冲突,该位置不可用{return false;}}return true;}void Queen::Backtrack() //迭代法实现回溯函数{x[1] = 0;int k = 1;while(k>0){x[k] += 1; //先将皇后放在第一列的位置上while((x[k]<=n)&&!(Place(k))) //寻找能够放置皇后的位置{x[k] += 1;}if(x[k]<=n) //找到位置{if(k == n) //如果寻找结束输出结果{/*for (int i=1;i<=n;i++){cout<<x[i]<<" ";}cout<<endl; */sum++;}else//没有结束则找下一行{k++;x[k]=0;}}else//没有找到合适的位置则回溯{ k--; }}}int nQueen(int n){Queen X; //定义Queen类的对象X//初始化XX.n=n;X.sum=0;int *p=new int[n+1];for(int i=0;i<=n;i++){p[i]=0;}X.x=p;X.Backtrack();delete []p;return X.sum; //返回不同解的个数}。
n后问题-回溯法

n后问题-回溯法问题描述: 在n*n的棋盘上放置彼此不受攻击的n个皇后。
按国际象棋的规则,皇后可以与之处在同⼀⾏或者同⼀列或同⼀斜线上的棋⼦。
n后问题等价于在n*n格的棋盘上放置n皇后,任何2个皇后不放在同⼀⾏或同⼀列的斜线上。
算法设计: |i-k|=|j-l|成⽴,就说明2个皇后在同⼀条斜线上。
可以设计⼀个place函数,测试是否满⾜这个条件。
1 当i>n时,算法搜索⾄叶节点,得到⼀个新的n皇后互不攻击放置⽅案,当前已找到的可⾏⽅案sum加1. 2 当i<=n时,当前扩展结点Z是解空间中的内部结点。
该结点有x[i]=1,2,3....n共n个⼉⼦节点。
对当前扩展结点Z的每个⼉⼦节点,由place检察其可⾏性。
并以深度优先的⽅式递归地对可⾏⼦树,或剪去不可⾏⼦树。
算法描述: #include <iostream>#include <cstdlib>using namespace std;class Queen{friend int nQueen(int);private:bool Place(int k);void Backtrack(int t);int n,* x;long sum;};bool Queen::Place(int k){for(int j=1;j<k;j++)if((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))return false;return true;}void Queen::Backtrack(int t){if(t>n)sum++;elsefor(int i=1;i<=n;i++){x[t] = i;if(Place(t))Backtrack(t+1);}}int nQueen(int n){Queen X;X.n = n;X.sum = 0;int *p = new int [n+1];for(int i=0;i<=n;i++)p[i] = 0;X.x = p;X.Backtrack(1);delete [] p;cout<<X.sum<<endl;return X.sum;}int main(){nQueen(4);nQueen(2);nQueen(3);return0;}执⾏结果:迭代回溯:数组x记录了解空间树中从根到当前扩展结点的路径,这些信息已包含了回溯法在回溯时所需要的信息。
回溯法求解N皇后问题

算法的实现
• 假设回溯法要找出所有的答案结点 。 • 设(x1,x2,…,xi-1)是状态空间树中由根到一个结 点的路径,而T(x1,…xi-1)是下述所有结点xi的 集合,它使得对于每一个xi,(x1,x2,…,xi)是由 根到一个结点xi的路径;假定还存在着一些限 界函数Bi,如果路径(x1,x2,…,xi)不可能延伸到 一个答案结点,则Bi(x1,x2,…,xi)取假值,否则 取真值。 • 于是解向量X(1:n)中的第i个分量,就是那些 选自集合T (x1,x2,…,xi-1)且使Bi为真的xi
HHIT
算法8.5:n-皇后问题的解
Algorithm
Procedure NQUEENS(n) //此过程使用回溯法求出一个n*n棋盘上放置n个皇后,使其不能互相攻 击的所有可能位置// integer k,n,X(1:n) X(1)0 ; k1 // k是当前行;X(k)是当前列 // while k>0 do // 对所有的行,执行以下语句 // X(k)X(k)+1 //移到下一列// while X(k)<=n and Not PLACE(k) do //此处能放这个皇后吗// X(k)X(k)+1 //不能放则转到下一列// repeat if X(k)<=n then //找到一个位置// if k=n then print (X) //是一个完整的解则打印这个数组// else kk+1;X(k)0 //否则转到下一行// end if else kk-1 //回溯// end if repeat End NQUEENS
HHIT
Algorithm
显然,棋盘的每一行上可以而且必须摆放一个皇后, 所以,n皇后问题的可能解用一个n元向量X=(x1, x2, …, xn) 表示,其中,1≤i≤n并且1≤xi≤n,即第i个皇后放在第i行第 xi列上。 由于两个皇后不能位于同一列上,所以,解向量X必 须满足约束条件: xi≠xj (式8.1)
n皇后 实验报告

n皇后实验报告n皇后实验报告引言:n皇后问题是一个经典的数学问题,其目标是在一个n×n的棋盘上放置n个皇后,使得它们互不攻击。
本实验旨在通过编程实现n皇后问题的解法,并对不同的算法进行性能分析。
实验方法:本实验采用Python语言编写程序,实现了两种常见的解法:回溯法和遗传算法。
回溯法是一种穷举搜索的方法,通过不断尝试每一种可能的放置方式,直到找到满足条件的解;而遗传算法则是通过模拟生物进化的过程,利用选择、交叉和变异等操作逐步优化解的质量。
实验结果:在实验中,我们分别测试了回溯法和遗传算法在不同规模的n皇后问题上的性能表现。
以下是实验结果的总结:1. 回溯法:- 对于规模较小的问题(n<10),回溯法可以在短时间内找到所有解,并输出结果。
- 随着问题规模的增大,回溯法的搜索时间呈指数级增长。
当n=15时,搜索时间已经超过10秒。
- 回溯法在解决大规模问题时,遇到了组合爆炸的问题,无法在合理的时间内得出结果。
2. 遗传算法:- 遗传算法对于规模较小的问题表现不如回溯法,因为其需要较长的时间来找到一个较优解。
- 随着问题规模的增大,遗传算法的性能逐渐超过回溯法。
当n=20时,遗传算法能够在合理的时间内找到一个较优解。
- 遗传算法在解决大规模问题时,相比回溯法有明显的优势,因为其搜索时间增长较慢。
实验讨论:通过对实验结果的分析,我们可以得出以下结论:- 回溯法适用于规模较小的n皇后问题,但在大规模问题上的性能不佳。
- 遗传算法在大规模问题上表现较好,但对于规模较小的问题需要更长的时间来找到较优解。
- 遗传算法的性能受到参数设置的影响,不同的选择、交叉和变异策略可能导致不同的结果。
结论:综上所述,回溯法和遗传算法都是解决n皇后问题的有效方法,但在不同规模的问题上有不同的性能表现。
在实际应用中,我们可以根据问题规模选择合适的算法来求解。
对于规模较小的问题,回溯法可以提供精确的解;而对于大规模问题,遗传算法能够在合理的时间内找到较优解。
回溯法求解N皇后问题

① 如果xi+1= ai+1k不是集合Si+1的最后一个元素,则令xi+1= ai+ 1k+1,即选择Si+1的下一个元素作为解向量X的第i+1个分量;
② 如果xi+1= ai+1k是集合Si+1的最后一个元素,就回溯到X=(x1, x2, …, xi),选择Si的下一个元素作为解向量X的第i个分量,假 设xi= aik,如果aik不是集合Si的最后一个元素,则令xi= aik+1; 否则,就继续回溯到X=(x1, x2, …, xi-1);
global X(1:k); integer i,k;
i1
while i<k do
if X(i)=X(k) or ABS(X(i)-X(k))=ABS(i-k) then
return (false)
end if
ii+1 repeat return (true)
判断是否有其它的皇 后与之在同一列或同 一斜对角线上
HHIT
Algorithm
(1)如果X=(x1, x2, …, xi+1)是问题的最终解,则输出这个解。 如果问题只希望得到一个解,则结束搜索,否则继续搜索其
他解;
(2)如果X=(x1, x2, …, xi+1)是问题的部分解,则继续构造解 向量的下一个分量;
(3)如果X=(x1, x2, …, xi+1)既不是问题的部分解也不是问题 的最终解,则存在下面两种情况:
while k>0 do // 对所有的行,执行以下语句 //
X(k)X(k)+1 //移到下一列//
while X(k)<=n and Not PLACE(k) do //此处能放这个皇后吗//
X(k)X(k)+1 //不能放则转到下一列//
回溯算法解决N皇后问题实验及其代码

实验报告4回溯算法实验4回溯算法解决N皇后问题一、实验目的1)掌握回溯算法的实现原理,生成树的建立以及限界函数的实现;2)利用回溯算法解决N皇后问题;二、实验内容回溯算法解决N皇后问题。
三、算法设计1)编写限界函数bool PLACE(int k,int x[]),用以确定在k列上能否放置皇后;2)编写void NQUEENS(int n)函数用以摆放N个皇后;3)编写主函数,控制输入的皇后数目;4)改进和检验程序。
四、程序代码//回溯算法解决N皇后问题的c++程序#include<math.h>#include<iostream>using namespace std;int count=0; //皇后摆放的可能性bool PLACE(int k,int x[]);//限界函数void NQUEENS(int n);//摆放皇后int main(){}int queen;cout<<"先生(女士)请您输入皇后的总数,谢谢!:"<<endl;cin>>queen;NQUEENS(queen);cout<<"所有可能均摆放完毕,谢谢操作"<<endl;return 0;void NQUEENS(int n){/*此过程使用回溯算法求出在一个n*n棋盘上放置n个皇后,使其即不同行,也不同列,也不在同一斜角线上*/int k, *x=new int[n];//存放皇后所在的行与列x[0]=0;k=0;while (k>=0&&k<n){ //对所有的行执行以下语句x[k]=x[k]+1; //移到下一列while(x[k]<=n&&(!PLACE(k,x))){ //此处能放置一个皇后吗?}if( x[k]<=n ) { //找到一个位置if( k==n-1 ){ //是一个完整的解吗cout<<"第"<<++count<<"排法是:"<<endl;for(int i=0;i<n;i++)//打印皇后的排列{}cout<<"\n";for (int j=0;j<n;j++){}cout<<"\n";if (x[i] == j+1){}else{}cout<<". ";cout<<"*";x[k]=x[k]+1; //移到下一列}}}}else { k=k+1; x[k]=0;} //移向下一行else k=k-1; //回溯bool PLACE(int k,int x[]){/*如果一个皇后能放在第k行和x(k)列,返回ture;否则返回false。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
A2 A1 A B3 A2 A1 B2 C2 B1 C1
A
跳马问题
4. 从C1出发,可以有三条路径,选择D1。 但到了D1以后,我们无路可走且D1也不 是最终目标点,因此,选择D1是错误的, D3 B3 我们退回C1重新选择D2。同样D2也是错 B2 C2 误的。再回到C1选择D3。D3只可以到E1, A2 但E1也是错误的。返回D3后,没有其他 C1 A1 B1 选择,说明D3也是错误的,再回到C1。 A 此时C1不再有其他选择,故C1也是错误 的,退回B1,选择C2进行尝试。
x
path:array[1..m] of integer; 其中,path[i]:表示第i个节点所走的方向
方向t,下一步的位置就是 (x+dx[t], y+dy[t])。
跳马问题
约束条件:
不越界: (x + dx[i] <= n) and (y + dy[i] > 0) and (y + dy[i] <= n)
回溯
Backtracking
回溯 N皇后问题 跳马问题 迷宫问题 图的着色问题 0-1背包问题 装载问题 批处理作业调度 填数问题 组合输出问题 算24点问题 ACM应用
学习要点
掌握回溯的概念 掌握经典问题的回溯解决方法 掌握回溯与其它方法的异同
回溯法
有许多问题,当需要找出它的解集或者要求回答什么 解是满足某些约束条件的最佳解时,往往要使用回溯 法。 ► 回溯法的基本做法是搜索,或是一种组织得井井有条 的,能避免不必要搜索的穷举式搜索法。这种方法适 用于解一些组合数相当大的问题。 ► 回溯法在问题的解空间树中,按深度优先策略,从根 结点出发搜索解空间树。算法搜索至解空间树的任意 一点时,先判断该结点是否包含问题的解。如果肯定 不包含,则跳过对该结点为根的子树的搜索,逐层向 其祖先结点回溯;否则,进入该子树,继续按深度优 先策略搜索。
跳马问题
分析: 按题意,马每一步可以有4种走法!
搜索过程: 1. 当马一开始位于左下角的时候,根据规 则,它只有两条线路可以选择(另外两 条超出棋盘的范围),我们无法预知该 走哪条,故任意选择一条,到达A1。 2.当到达A1点后,又有三条线路可以 选 择,于是再任意选择一条,到达B1。 3.从B1再出发,又有两条线路可以选择, 先选一条,到达C1。
注意:同一个问题可以有多种表示,有些表示方法更简单, 所需表示的状态空间更小(存储量少,搜索方法简单)。
n=3时的0-1背包问题用完全二叉树表示的解空间
生成问题状态的基本方法
► ► ► ►
► ►
扩展结点:一个正在产生儿子的结点称为扩展结点 活结点:一个自身已生成但其儿子还没有全部生成的节点称 做活结点 死结点:一个所有儿子已经产生的结点称做死结点 深度优先的问题状态生成法:如果对一个扩展结点R,一旦 产生了它的一个儿子C,就把C当做新的扩展结点。在完成 对子树C(以C为根的子树)的穷尽搜索之后,将R重新变成 扩展结点,继续生成R的下一个儿子(如果存在) 宽度优先的问题状态生成法:在一个扩展结点变成死结点 之前,它一直是扩展结点 回溯法:为了避免生成那些不可能产生最佳解的问题状态, 要不断地利用限界函数(bounding function)来处死那些实 际上不可能产生所需解的活结点,以减少问题的计算量。 具有限界函数的深度优先生成法称为回溯法
回溯部份: 即状态恢复,使其恢复到进 入该分支前的状态,继续新的 分支 x[k]:=0; Dec(k);
程序实现: 回溯算法可用非递归和递归两种方法实现!
N皇后问题
非递归写法: 算法描述: Nqueens() 1. 产生一种新放法 begin 2. 冲突,继续找,直到找到不冲 Flag ← true x[1] ← 0 突----不超范围 3. if 不冲突 then k<nk+1 k←1 k=n一组解 while k>0 do While flag do 4. if 冲突 then 回溯 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 if k=n then sum ← sum+1 if k=n then print;flag ←false else begin k ← k+1 x[k] ← 0 end else k ← k-1 end end
{每层均有n种放法} {寻找放置皇后的位置}
{放置皇后) {8个皇后都放置好,输出} {若只想找一组解,halt} {继续递归放置下一个皇后}
end;
跳马问题
在n×m棋盘上有一中国象棋中的马: 1. 马走日字; 2. 马只能往右走。 请你找出一条可行路径,使得马可以从棋盘的左下角(1,1)走 到右上角(n,m)。 输入:9 5 输出:(1,1)->(3,2)->(5,1)->(6,3)->(7,1)->(8,3)->(9,5)
K=1
K=2
* * *** * *
回溯
*
*
出解后 可以继 续刚才 的做法
K=3
* * * * ** * * * * * * * * * * *
回溯
*
*
* *
*
K=4
* *
*
N皇后问题
程序结束条件: 一组解:设标志,找到一解后更改标志,以标志做为结束循环的条 件所有解:k=0
判断约束函数 Function Place(k:integer):boolean; place:=true; for j←1 to k-1 do if |k-j|=|x[j]-x[k]| or x[j]=x[k] then place:= false
N皇后问题
在一个n*n的国际象棋棋盘上放置n个皇后,使得它们中任意2个之间都 不互相“攻击”,即任意2个皇后不可在同行、同列、同斜线上。 输出N,⑴求N皇后问题的一种放法;
⑵求N皇后问题的所有放法
分析: N=4时,右图是一组解
要素一: 解空间
一般想法:利用二维数组,用[i,j]确定一个皇后位置!
优化:利用约束条件,只需一维数组即可! x:array[1..n] of integer; x[i]:i表示第i行皇后 x[i]表示第i行上皇后放第几列
迭代回溯
采用树的非递归深度优先遍历算法,可将回溯法表示为一个非 递归迭代过程。
void iterativeBacktrack () { int t=1; while (t>0) { if (f(n,t)<=g(n,t)) for (int i=f(n,t);i<=g(n,t);i++) { x[t]=h(i); if (constraint(t)&&bound(t)) { if (solution(t)) output(x); else t++;} } else t--; } }
►
回溯法
回溯算法是所有搜索算法中最为基本的一种算法,是一 种能避免不必要搜索的穷举式的搜索算法,其基本思想 就是穷举搜索。 算法思想: 采用了一种“走不通就掉头”的思想。搜索时往往有多分支, 按某一分支为新的出发点,继续向下探索,当所有可能情况都 探索过且都无法到达目标的时候,再回退到上一个出发点,继 续探索另一个可能情况,这种不断回头寻找目标的方法称为 “回溯法”。
N皇后问题
递归写法: procedure try(k:byte); var i:byte; begin for i:=1 to n do if place(k) then begin x[k]:=i; if k=n then print else try(k+1); end;
分析:状态恢复(回溯)在什 么地方实现?
要素三:状态树
将搜索过程中的每个状态用树的形式表示出来! 画出状态树对书写程序有很大帮助!
K=0
过程:进入新一行, 该行上按顺序逐个 格子尝试,直到能 放为止(不冲突、 不越界)
N皇后问题
* **
算法描述: 1. 产生一种新放法 2. 冲突,继续找,直到找到不冲 突----不超范围 3. if 不冲突 then k<nk+1 k=n一组解 4. if 冲突 then 回溯
D2 E1 D1
5. 从C2出发,有四条路径可以选择,选择 D4,从D4出发又有两条路径,选择E1错 误,返回D4选择E2,从E2出发有两条路 径,先选择F1错误,返回E2选择B,而B 恰好是我们要到达的目标点,至此,一 条路径查找成功。
B3 A2 B2 C2 A1 B1 D4 E2
BAE1 F1来自马问题④③ ② ①
⑤
⑤ ⑥
④
② ① ③
Path[5]:=1
④
⑤ ⑥
⑤ ⑥
⑦
⑤
⑤
跳马问题
跳马问题(非递归)
Horse() begin path[1] ← 0,k ← 1,x ←1,y ←1 while (x<>m) and (y<>n) do begin path[k] ←path[k] +1 while (x + dx[i] <= n) and (y + dy[i] > 0) and (y + dy[i] <= n) and (path[k]<=4) do path[k] ← path[k] +1 if path[k]<=4 then {在4种走法中找到不越界的走法} begin x ←x+dx[i],y ←y+dy[i] if (x<>m) and (y<>n) then print else begin k ← k+1 path[k] ← 0 end end else path[k] ← 0,k ← k-1 end end