回溯法求解N皇后问题

合集下载

回溯法实验(n皇后问题)(迭代法)

回溯法实验(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格的棋盘上放置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皇后问题Word版

回溯算法与八皇后问题N皇后问题Word版

回溯算法与八皇后问题(N皇后问题)1 问题描述八皇后问题是数据结构与算法这一门课中经典的一个问题。

下面再来看一下这个问题的描述。

八皇后问题说的是在8*8国际象棋棋盘上,要求在每一行放置一个皇后,且能做到在竖方向,斜方向都没有冲突。

更通用的描述就是有没有可能在一张N*N的棋盘上安全地放N个皇后?2 回溯算法回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

在现实中,有很多问题往往需要我们把其所有可能穷举出来,然后从中找出满足某种要求的可能或最优的情况,从而得到整个问题的解。

回溯算法就是解决这种问题的“通用算法”,有“万能算法”之称。

N皇后问题在N增大时就是这样一个解空间很大的问题,所以比较适合用这种方法求解。

这也是N皇后问题的传统解法,很经典。

下面是算法的高级伪码描述,这里用一个N*N的矩阵来存储棋盘:1) 算法开始, 清空棋盘,当前行设为第一行,当前列设为第一列2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步3) 在当前位置上满足条件的情形:在当前位置放一个皇后,若当前行是最后一行,记录一个解;若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;若当前行是最后一行,当前列不是最后一列,当前列设为下一列;若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;以上返回到第2步4) 在当前位置上不满足条件的情形:若当前列不是最后一列,当前列设为下一列,返回到第2步;若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置,返回到第2步;算法的基本原理是上面这个样子,但不同的是用的数据结构不同,检查某个位置是否满足条件的方法也不同。

回溯法求解N皇后问题

回溯法求解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皇后问题
return false;
return true;
}
void queue(intn)
{
inti,k;
for(i=1;i<=n;i++)
x[i]=0;
k=1;
while(k>=1)
{
x[k]=x[k]+1; //在下一列放置第k个皇后
while(x[k]<=n&&!place(k))
x[k]=x[k]+1;//搜索下一列
k=k+1;//放置下一个皇后
else
{
x[k]=0;//重置x[k],回溯
k=k-1;
}
}
}
void main()
{
intn;
printf("输入皇后个数n:\n");
scanf("%d",&n);
queue(n);
}
五、实验结果截图
六、实验总结
关于n皇后问题,看似复杂难懂,运行结果也很多,但是如果掌握了算法的要点,并且编写的准确无误,其实很简单明了的,而且在组实验的过程中会体会到很多乐趣,当然也有不懂得地方,需要请教别人,总之受益匪浅。
if(x[k]<=n&&k==n)//得到一个输出
{
for(i=1;i<=n;i++)
printf("%d ",x[i]);
printf("\n");
//return;//若return则只求出其中一种解,若不return则可以继续回溯,求出全部的可能的解
}
else if(x[k]<=n&&k<n)

回溯典型题目——N皇后问题剖析

回溯典型题目——N皇后问题剖析

N皇后问题问题描述:在N*N的方格中放置N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上)对于给定的N,输出摆放方案并求出有多少种合法的放置方法。

【假设N<=10】基础:四皇后问题我们先来看看四皇后问题,在一个4*4的棋盘中摆放4个皇后,四个皇后不能摆在互相攻击的位置。

方案一:回溯法(程序中包含递归和深搜)源代码://四皇后问题:回溯#include <stdio.h>#include <string>int flag[4][4]; //用于标记放过的棋子//n个皇后,深搜int count=0;int iscorrect(int i,int j){ //判断是否可以放置棋子int a,b;for(a=i,b=0;b<4;b++){if(flag[a][b]==1) //说明在同一行有棋子return 0;}for(a=0,b=j;a<4;a++){if(flag[a][b]==1) //说明在同一行有棋子return 0;}for(a=i-1,b=j-1;a>=0&&b>=0;a--,b--){ //左上方if(flag[a][b]==1)return 0;}for(a=i-1,b=j+1;a>=0&&b<=3;a--,b++){ //左下方if(flag[a][b]==1)return 0;}for(a=i+1,b=j-1;a<=3&&b>=0;a++,b--){ //右上方if(flag[a][b]==1)return 0;}for(a=i+1,b=j+1;a<=3&&b<=3;a++,b++){ //判断右下方if(flag[a][b]==1)return 0;}return 1;}void DFSQ(int i){int m,n;int j;//i代表行数,j代表列数if(i==4){ //因为棋盘是(n-1)*(n-1)模式的,而i是行,当棋盘到第四行的时候,表明已//经完成0~3的所有排布已经完成for(m=0;m<4;m++){for(n=0;n<4;n++){printf("%d ",flag[m][n]);}printf("\n");}count++;printf("\n");return; //不要忘记这个}else{for(j=0;j<4;j++){if(iscorrect(i,j)){ //如果可以放置棋子flag[i][j]=1; //标记flag[i][j]DFSQ(i+1); //递归调用flag[i][j]=0; //消除标记}}}}int main(){memset(flag,0,sizeof(flag));DFSQ(0);printf("count=%d\n",count);return 0;}其实从四皇后问题拓展到n皇后问题是非常简单的事情,方案一进阶到N皇后的源代码:N皇后其实只要把其中的4改成N就行了:源代码:#include <stdio.h>#include <string>int flag[10][10]; //用于标记放过的棋子int number; //表示棋子个数//n个皇后,深搜int count=0;int iscorrect(int i,int j){ //判断是否可以放置棋子int a,b;for(a=i,b=0;b<number;b++){if(flag[a][b]==1) //说明在同一行有棋子return 0;}for(a=0,b=j;a<number;a++){if(flag[a][b]==1) //说明在同一行有棋子return 0;}for(a=i-1,b=j-1;a>=0&&b>=0;a--,b--){ //左上方if(flag[a][b]==1)return 0;}for(a=i-1,b=j+1;a>=0&&b<=number-1;a--,b++){ //左下方if(flag[a][b]==1)return 0;}for(a=i+1,b=j-1;a<=number-1&&b>=0;a++,b--){ //右上方if(flag[a][b]==1)return 0;}for(a=i+1,b=j+1;a<=number-1&&b<=number-1;a++,b++){ //判断右下方if(flag[a][b]==1)return 0;}return 1;}void DFSQ(int i){int m,n;int j;//i代表行数,j代表列数if(i==number){ //因为棋盘是(n-1)*(n-1)模式的,而i是行,当棋盘到第四行的时候,表明已经完成0~number-1的所有排布已经完成for(m=0;m<number;m++){for(n=0;n<number;n++){printf("%d ",flag[m][n]);}printf("\n");}count++;printf("\n");return; //不要忘记这个}else{for(j=0;j<number;j++){if(iscorrect(i,j)){ //如果可以放置棋子flag[i][j]=1; //标记flag[i][j]DFSQ(i+1); //递归调用flag[i][j]=0; //消除标记}}}}int main(){scanf("%d",&number);memset(flag,0,sizeof(flag));DFSQ(0);printf("count=%d\n",count);return 0;}。

回溯算法解决N皇后问题实验及其代码

回溯算法解决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。

回溯法解决N皇后问题C语言

回溯法解决N皇后问题C语言

回溯法解决N皇后问题C语⾔问题描述:⼋皇后问题是⼀个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置⼋个皇后,使得任何⼀个皇后都⽆法直接吃掉其他的皇后?为了达到此⽬的,任两个皇后都不能处于同⼀条横⾏、纵⾏或斜线上。

回溯法:回溯法⼜称试探法。

回溯法的基本做法是深度优先搜索。

即从⼀条路往前⾛,能进则进,不能进则退回来,换⼀条路再试。

源代码:#include<stdio.h>#include<math.h>int x[9]={0};bool PLACE(int k)//检测第k个皇后能否放进棋盘{int i=1;while(i<k){if(x[i]==x[k]||fabs(x[i]-x[k])==fabs(i-k))return false;i++;}return true;}void NQUEENS(int n){int i,k=1; //k为当前⾏号x[1]=0;//x[k]为第k⾏皇后所放的列号while(k>0){x[k]++;while(x[k]<=n&&!PLACE(k))//该⾏不符合,则放⼊下⼀⾏x[k]++;if(x[k]<=n){if(k==n)//输出x[]{for(i=1;i<=n;i++)printf("x[%d]:%d ",i,x[i]);printf("\n");}else//判断下⼀⾏{k++; x[k]=0;}}else k--;//没找到,则回溯}return ;}int main(){NQUEENS(8);return0;}。

回溯法之N皇后问题(C语言)

回溯法之N皇后问题(C语言)

//回溯法之N皇后问题当N>10,就有点抽了~~/*结果前total行每行均为一种放法,表示第i行摆放皇后的列位置,第total+1行,输出total*/#include<stdio.h>#include<stdlib.h>int n,stack[100]; //存当前路径int total; //路径数void make(int l) //递归搜索以stack[l]为初结点的所有路径{int i,j; //子结点个数if (l==n+1){total=total+1; //路径数+1for(i=1;i<=n;i++)printf("%-3d",stack[i]); //输出第i行皇后的列位置stack[i] printf("\n");exit; //回溯(若试题仅要求一条路径,则exit改为halt即可)}for (i=1;i<=n;i++){stack[l]=i; //算符i作用于生成stack[l-1]产生子状态stack[l];if (!att(l,i)) make(l+1);} //再无算符可用,回溯}int att(int l,int i){int k;for (k=1;k<l;k++)if (abs(l-k)==abs(stack[k]-i)||i==stack[k]) return 1;return 0;}int main(){printf("N=");scanf("%d",&n);total=0; //路径数初始化为0make(1); //从结点1出发,递归搜索所有的路径printf("%d\n",total);system("pause");return 0;}由回溯法的算法流程可以看出,除非边界条件设置不当而导致死循环外,回溯法一般是不会产生内存溢出的。

基本算法4-回溯法-N皇后问题

基本算法4-回溯法-N皇后问题

1
x1=1 2
x2= 2 3 kill
1
1 2
回溯到结点2生成结点8, 路径变为(1, 3), 则结点8成为E-结点, 它生成结点9和结点11都会被杀死(即它的儿子表示不可能导 x1=1 致答案的棋盘格局), 所以结点8也被杀死, 应回溯.
1
1
1
2
2
3
x2= 2 x2= 3 3 kill 8 x3=2 9 kill x3=4 11 kill
借书问题 [问题描述]
学校放暑假时,信息学辅导教师有n本书要分给参加培训的n个学生。如:A, B,C,D,E共5本书要分给参加培训的张、刘、王、李、孙5位学生,每人只能选 1本。教师事先让每个人将自己喜爱的书填写在如下的表中,然后根据他们填写的 表来分配书本,希望设计一个程序帮助教师求出可能的分配方案,使每个学生都满 意。 A 张 王 刘 孙 李 Y Y Y Y Y Y Y B C Y D Y E
● ●
……
● ● ● ● ● ● ● ●
……
……





● ● ● ●


● ●

● ●

● ●
● ● ● ●
● ● ● ●
● ● ● ●
● ● ● ● ●
● ● ●
•搜索解空间,剪枝:
– (1) 从空棋盘起,逐行放置棋子。
– (2) 每在一个布局中放下一个棋子,即推演到一 个新的布局。 – (3) 如果当前行上没有可合法放置棋子的位置, 则回溯到上一行,重新布放上一行的棋子。
61 2
64
3
16
3
20
4
22
1
25

一种求解n皇后问题的概率回溯复合算法

一种求解n皇后问题的概率回溯复合算法

n皇后问题是一种经典的搜索问题,它的目标是在n*n的棋盘上放置n个皇后,使得这n 个皇后互不攻击(不在同一行、同一列、同一对角线上)。

求解n皇后问题的概率回溯复合算法是一种搜索算法,它通过在回溯过程中加入概率元素,来提高求解n皇后问题的效率。

具体来说,该算法的基本思路是:
初始化:将棋盘初始化为一个n*n的空棋盘,并设置一个计数器count,用于记录已经放置的皇后的数量。

回溯:从第0行开始,依次枚举每一行的每一列,判断是否可以在该位置放置皇后。

如果可以,就在该位置放置皇后,并将计数器count加1。

如果不可以,就跳过该位置,继续枚举下一列。

概率元素:在枚举每一列时,随机生成一个概率值p(0<=p<=1)。

如果p大于0.5,则在该位置放置皇后;否则跳过该位置,继续枚举下一列。

这样,在回溯过程中加入了一定的随机性,使得算法具有一定的概率元素。

终止条件:如果count的值等于n,则说明已经成功放置了n个皇后,可以终止算法。

否则,继续执行下一行的枚举过程。

复合:在每一行枚举完所有列之后,需要将已经放置的皇后撤销,并将计数器count减1,然后回到上一行,继续枚举下一列。

这样,在回溯的过程中,算法会不断地撤销和放置皇后,从而实现复合的效果。

总之,求解n皇后问题的概率回溯复合算法是一种结合了回溯和概率元素的搜索算法,可以有效地提高求解n皇后问题的效率。

用回溯法求解n皇后问题

用回溯法求解n皇后问题

c程序实现
分析问题
//求解的递归函数 void Queen(int i,int n) { if(i>n) Output(); else { for(int j=1;j<=n;++j) // j代表列值 { int k=1; x[i]=j;//重新换一个列值,这里就是体现回溯的地方 while(k<i) { if((x[k]-x[i])*(abs(x[k]-x[i])-abs(k-i))!=0)
分析问题
问题分析
如何保证任何两个皇后不再一 条斜线上?设两个皇后q1和q2放 在(i,j)和(k,l)位置上,如 果q1和q2在斜率为-1的对角线上, 那么i - j = k - l成立,如果在斜率 为1的对角线上,那么 i + j = k + l成立,由此可知只要 | i - k | ≠ | j - l |成立,q1和q2就不 再同一条斜线上。 |i-k|≠|j-l|
分析问题
2.确定解空间 用完全n叉树表示解空间,现在以n=4为例:
分析问题
问题分析
1
1
2 1 34
3 1 24
4 123
2 34
× ×× ×34 24 23 ×14 13 × 34 ×

24 14 12 23 13 12

4 3 4 2 3 2 4 3 4 1 3 1 4 2 41 21 3 2 31 2 1
回溯法的基本思想
回溯法的基本思想是在问题的解空间树上按 深度优先搜索策略,从根节点出发搜索整个解 空间。搜索过程中,每到达一个结点时,则判 断该结点为根的子树是否含有问题的解,如果 可以确定该子树中不含有问题的解,则放弃对 该子树的搜索,逐层向其祖先节点回溯。否则, 进入该子树。

n皇后问题—回溯法

n皇后问题—回溯法

n皇后问题—回溯法⼀、问题简介描述在n×n 格的棋盘上放置彼此不受攻击的n 个皇后。

按照国际象棋的规则,皇后可以攻击与之处在同⼀⾏或同⼀列或同⼀斜线上的棋⼦。

n后问题等价于在n×n格的棋盘上放置n个皇后,任何2 个皇后不放在同⼀⾏或同⼀列或同⼀斜线上。

设计⼀个解n 后问题的队列式分⽀限界法,计算在n× n个⽅格上放置彼此不受攻击的n个皇后的⼀个放置⽅案。

Input输⼊数据只占⼀⾏,有1 个正整数n,n≤20。

Output将计算出的彼此不受攻击的n个皇后的⼀个放置⽅案输出。

第1⾏是n个皇后的放置⽅案。

Sample Input5Sample Output1 3 52 4⼆、问题分析回溯法解的⽣成回溯法对任⼀解的⽣成,⼀般都采⽤逐步扩⼤解的⽅式。

每前进⼀步,都试图在当前部分解的基础上扩⼤该部分解。

它在问题的状态空间树中,从开始结点(根结点)出发,以深度优先搜索整个状态空间。

这个开始结点成为活结点,同时也成为当前的扩展结点。

在当前扩展结点处,搜索向纵深⽅向移⾄⼀个新结点。

这个新结点成为新的活结点,并成为当前扩展结点。

如果在当前扩展结点处不能再向纵深⽅向移动,则当前扩展结点就成为死结点。

此时,应往回移动(回溯)⾄最近的活结点处,并使这个活结点成为当前扩展结点。

回溯法以这种⼯作⽅式递归地在状态空间中搜索,直到找到所要求的解或解空间中已⽆活结点时为⽌。

回溯法与穷举法回溯法与穷举法有某些联系,它们都是基于试探的。

穷举法要将⼀个解的各个部分全部⽣成后,才检查是否满⾜条件,若不满⾜,则直接放弃该完整解,然后再尝试另⼀个可能的完整解,它并没有沿着⼀个可能的完整解的各个部分逐步回退⽣成解的过程。

⽽对于回溯法,⼀个解的各个部分是逐步⽣成的,当发现当前⽣成的某部分不满⾜约束条件时,就放弃该步所做的⼯作,退到上⼀步进⾏新的尝试,⽽不是放弃整个解重来。

解题思路⽤ d[i]=k 表⽰第 i 个皇后放在第 k 个位置上,然后从第1个皇后,第1个位置开始,每次放置前先调⽤ check() 函数判断与其他皇后是否冲突如果不冲突则放置如果冲突则移⾄下⼀个位置,如果位置到了最后⼀个,则不放,且将上⼀次放置的皇后移⾄下⼀个位置,递归调⽤。

回溯法之N皇后问题

回溯法之N皇后问题

回溯法之N皇后问题回溯法之N皇后问题1. 问题描述在n*n格的棋盘上放置彼此不受攻击的n个皇后。

按照国际象棋的规则,皇后可以攻击与之在同⼀⾏或同⼀列或同⼀斜线上的旗⼦。

n后问题等价于在n*n格的棋盘上放置n个皇后,任何2个皇后不放在同⼀⾏或同⼀列或同⼀斜线上。

2. 问题分析(以n=4皇后问题为例)有俩种解法,第⼀种采⽤解空间为N(4)叉树的解法、第⼆种是采⽤解空间为排列数的解法。

2.1. N(4)叉树的解法每个皇后在⼀⾏上有四个可选位置。

即每个⾮叶结点有4个⼦节点,4叉树如下:解向量:(x1,x2,x3,......,x n)显约束:任意俩皇后不同⾏。

隐约束:(1) 不同列:x i ≠ x j (2) 不处于同⼀正反对⾓线:|i - j| ≠ |x i - x j|核⼼代码:// 剪枝函数,排除同列和同⼀对⾓线的分⽀int place1(int k) {for (int j = 1; j < k; j++)if (abs(k - j) == abs(x[j] - x[k]) || x[j] == x[k])return 0;return 1;}// t > n代表当前解已经求出,将总数+1// 利⽤循环遍历节点的n叉,同时判断分叉是否符合条件// 符合条件的分叉继续遍历下去void BackTrack1(int t) {if (t > n)sum++;elsefor (int i = 1; i <= n; i++) {x[t] = i;if (place1(t))BackTrack1(t + 1);}}2.2 排列数的解法解向量:(x1,x2,x3,......,x n)显约束:任意俩皇后不同⾏、不同列。

x1,x2,x3,......,x n是1,2,3.......n排列隐约束:不处于同⼀正反对⾓线:|i - j| ≠ |x i - x j|核⼼代码:// 交换俩⾏皇后的位置// 实现切换排列数的分⽀作⽤void swap(int i, int j) {int tmp = x[i];x[i] = x[j];x[j] = tmp;}// 剪枝函数,排除在同⼀对⾓线上的情况int place2(int k) {for (int j = 1; j < k; j++)if (abs(k - j) == abs(x[j] - x[k]))return 0;return 1;}// t > n时表⽰当前排列符合条件,总数 + 1// 利⽤for循环,和swap函数,将节点对应的所有排列遍历⼀次// 同时采⽤剪枝函数,减去错误的分⽀// 对正确的分⽀继续求解下去// 最后递归求解结束后,再次调⽤swap函数将状态返回到原本的节点状态void BackTrack2(int t) {if (t > n) sum++;elsefor (int i = t; i <= n; i++) {swap(t, i);if (place2(t))BackTrack2(t + 1);swap(t ,i);}}3. 完整代码/*** 回溯法求解n皇后问题* 使⽤x解向量,x1,x2,x3分别表⽰在1,2,3⾏上皇后的列号**/#include <stdio.h>#include <stdlib.h>#define MAX 4/*** n 皇后个数* x 当前解* sum**/int n = MAX;int x[MAX + 1];long sum = 0;// 剪枝函数,排除同列和同⼀对⾓线的分⽀int place1(int k) {for (int j = 1; j < k; j++)if (abs(k - j) == abs(x[j] - x[k]) || x[j] == x[k])return 0;return 1;}// t > n代表当前解已经求出,将总数+1// 利⽤循环遍历节点的n叉,同时判断分叉是否符合条件// 符合条件的分叉继续遍历下去void BackTrack1(int t) {if (t > n)sum++;elsefor (int i = 1; i <= n; i++) {x[t] = i;if (place1(t))BackTrack1(t + 1);}}// 交换俩⾏皇后的位置// 实现切换排列数的分⽀作⽤void swap(int i, int j) {int tmp = x[i];x[i] = x[j];x[j] = tmp;}// 剪枝函数,排除在同⼀对⾓线上的情况int place2(int k) {for (int j = 1; j < k; j++)if (abs(k - j) == abs(x[j] - x[k]))return 0;return 1;}// t > n时表⽰当前排列符合条件,总数 + 1// 利⽤for循环,和swap函数,将节点对应的所有排列遍历⼀次// 同时采⽤剪枝函数,减去错误的分⽀// 对正确的分⽀继续求解下去// 最后递归求解结束后,再次调⽤swap函数将状态返回到原本的节点状态void BackTrack2(int t) {if (t > n) sum++;elsefor (int i = t; i <= n; i++) {swap(t, i);if (place2(t))BackTrack2(t + 1);swap(t ,i);}}void main() {for (int i = 0; i <= n; i++)x[i] = i;BackTrack1(1);printf("%d\n", sum);for (int i = 0; i <= n; i++)x[i] = i;sum = 0;BackTrack2(1);printf("%d\n", sum);system("pause");}。

用回溯法实现n皇后问题

用回溯法实现n皇后问题

今天学习了一个经典的算法题:N皇后问题。

在N*N格的棋盘上放置N个皇后,使这些皇后不能互相攻击,即皇后不能在同一行,同一列或者同一对角线上。

以下是看到的比较经典的算法。

解决该问题的最典型的算法就是回溯法。

在那些涉及到寻找一组解的问题或者求满足某些约束条件的最优解的问题中,有许多可以用回溯法来求解。

回溯法是一个既带有系统性又带有跳跃性的的搜索算法。

它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。

算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。

如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。

否则,进入该子树,继续按深度优先的策略进行搜索。

回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。

而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。

这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。

回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。

这个开始结点就成为一个活结点,同时也成为当前的扩展结点。

在当前的扩展结点处,搜索向纵深方向移至一个新结点。

这个新结点就成为一个新的活结点,并成为当前扩展结点。

如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。

换句话说,这个结点不再是一个活结点。

此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。

回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。

运用回溯法解题通常包含以下三个步骤:(1)针对所给问题,定义问题的解空间;(2)确定易于搜索的解空间结构;(3)以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索;回溯法可用递归实现:procedure try(i:integer);varbeginif i>n then 输出结果else for j:=下界 to 上界 dobeginx[i]:=h[j];if 可行{满足限界函数和约束条件} then begin 置值;try(i+1); end;end;end;回到N皇后问题的解决来,看看如何用回溯法解。

回溯法解决n皇后问题

回溯法解决n皇后问题

n 皇 后 问 题N 皇后问题,是一个古老而着名的问题,是回溯算法的典型例题:在N*N 格的格子上摆放N 个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法? 1、定义问题的解空间首先以八皇后为例,可以用一棵树表示8皇后问题的解空间。

由于8皇后问题的解空间为8!种排列,因此我们将要构造的这棵树实际上是一棵排列树。

2、确定解空间树的结构给棋盘上的行和列从1到8编号,同时也给皇后从1到8编号。

由于每一个皇后应放在不同的行上,不失一般性,假设皇后i 放在第i 行上,因此8皇后问题可以表示成8元组(x 1, x 2, …, x 8), 其中xi (i =1, 2, …, 8)表示皇后i 所放置的列号。

这种表示法的显式约束条件是S i ={1, 2, 3, 4, 5, 6, 7, 8},i =1, 2, …, 8。

在这种情况下, 解空间为88个8元组组成,而隐式约束条件是没有两个xi 相同(即所有皇后必须在不同列上),且满足不存在两个皇后在同一条对角线上。

加上隐式约束条件,问题的解空间可进一步减小。

此时,解空间大小为8!,因为所有解都是8元组的一个置换。

图5-7表示了8皇后问题的一个解。

图5-7 8皇后问题的一个解为了简单起见,图5-8只给出了n =4时问题的一种可能树结构。

QQQQQQQQ8765432112345678图5-8 4皇后问题解空间的树结构在实际中,并不需要生成问题的整个状态空间。

通过使用限界函数来删除那些还没有生成其所有子结点的活结点。

如果用(x1,x2,…,x i)表示到当前E结点的路径,那么xi+1就是这样的一些结点,它使得(x1,x2,…,x i,x i+1)没有两个皇后处于相互攻击的棋盘格局。

在4皇后问题中,惟一开始结点为根结点1,路径为( )。

开始结点既是一个活结点,又是一个E结点,它按照深度优先的方式生成一个新结点2,此时路径为(1),这个新结点2变成一个活结点和新的E结点,原来的E结点1仍然是一个活结点。

N皇后问题求解

N皇后问题求解

剪枝函数:
public static boolean check(int n) { for (int i = 1; i < n; i++) { if (a[i] == a[n] || (Math.abs(i - n) == Math.abs(a[i] - a[n]))) return false; } return true; } //分枝限界函数,其中,a[i]=a[n]表示皇后所// 在的列相等;//Math.abs(i - n) == //Math.abs(a[i] - a[n]))判断两皇后是否在同一 //对角线上。
2
(3,1) (3,2) (3,3) (3,4)) (4,4)
4
用n元组x[1:n]表示n皇 后问题的解,x[i]表示皇 后i放在第i 行的第x[i]列 上,用完全n叉树表示 解空间。 剪枝函数设计:对于两个皇后A(i,j)、B(k,l) 两个皇后不同行:i不等于k; 两个皇后不同列:j不等于l; 两个皇后不同一条斜线|i-k|≠|j-l|.
3,代码及运行结果
一、代码见“N皇后问题求解代码.doc”文档;
二、运行结果见“N皇后问题求解代码运行结 果.mp4”。
Thanks for your attention !!!
N皇后问题,是一个古老而著名
的问题,是回溯算法的典型例题: 在N*N格的格子上摆放N个皇后, 使其不能互相攻击,即任意两个 皇后都不能处于同一行、同一列 或同一斜线上,问有多少种摆法?
N=4时,可能的树结构:
通过限界条件我们可以得到如 下的搜索树:
当N=4时,即4皇后问题;下 图为4皇后的一种解:
在第1的m行为合理配置的基础上再配置第m1行直至第n行也是合理时就找到了一在每行上顺次从第一列到第n列配置当第n列也找不到一个合理的配置时就要回溯去改变前一列的配置

实验报告:回溯法求解N皇后问题(Java实现)

实验报告:回溯法求解N皇后问题(Java实现)

实验报告一、实验名称:回溯法求解‎N皇后问题‎(Java实‎现)二、学习知识:回溯法:也称为试探‎法,它并不考虑‎问题规模的‎大小,而是从问题‎的最明显的‎最小规模开‎始逐步求解‎出可能的答‎案,并以此慢慢‎地扩大问题‎规模,迭代地逼近‎最终问题的‎解。

这种迭代类‎似于穷举并‎且是试探性‎的,因为当目前‎的可能答案‎被测试出不‎可能可以获‎得最终解时‎,则撤销当前‎的这一步求‎解过程,回溯到上一‎步寻找其他‎求解路径。

为了能够撤‎销当前的求‎解过程,必须保存上‎一步以来的‎求解路径,这一点相当‎重要。

三、问题描述N皇后问题‎:在一个 N * N 的国际象棋‎棋盘中,怎样放置 N 个皇后才能‎使N 个皇后之间‎不会互相有‎威胁而共同‎存在于棋局‎中,即在 N * N 个格子的棋‎盘中没有任‎何两个皇后‎是在同一行‎、同一列、同一斜线上‎。

深度优先遍‎历的典型案‎例。

四、求解思路1、求解思路:最容易想到‎的方法就是‎有序地从第‎1列的第 1 行开始,尝试放上一‎个皇后,然后再尝试‎第2 列的第几行‎能够放上一‎个皇后,如果第 2 列也放置成‎功,那么就继续‎放置第 3 列,如果此时第‎3列没有一行‎可以放置一‎个皇后,说明目前为‎止的尝试是‎无效的(即不可能得‎到最终解),那么此时就‎应该回溯到‎上一步(即第 2 步),将上一步(第 2 步)所放置的皇‎后的位置再‎重新取走放‎在另一个符‎合要求的地‎方…如此尝试性‎地遍历加上‎回溯,就可以慢慢‎地逼近最终‎解了。

2、需要解决的‎问题:如何表示一‎个N * N 方格棋盘能‎够更有效?怎样测试当‎前所走的试‎探路径是否‎符合要求?这两个问题‎都需要考虑‎到使用怎样‎的数据结构‎,使用恰当的‎数据结构有‎利于简化编‎程求解问题‎的难度。

3、我们使用以‎下的数据结‎构:int colum‎n[col] = row 表示第 col 列的第 row 行放置一个‎皇后boole‎an rowEx‎i sts[i] = true 表示第 i 行有皇后boole‎an a[i] = true 表示右高左‎低的第 i 条斜线有皇‎后(按→↓顺序从1~ 2*N -1 依次编号)boole‎an b[i] = true 表示左高右‎低的第 i 条斜线有皇‎后(按→↑顺序从1~ 2*N -1 依次编号)五、算法实现对应这个数‎据结构的算‎法实现如下‎:1.**2. * 回溯法求解‎N 皇后问题3. * @autho‎r haoll‎o yin4. */5.publi‎c class‎N_Que‎e ns {6.7.// 皇后的个数‎8. priva‎t e int queen‎s Num = 4;9.10.// colum‎n[i] = j表示第 i 列的第 j 行放置一个‎皇后11. priva‎t e int[] queen‎s = new int[queen‎s Num + 1];12.13.// rowEx‎i sts[i] = true 表示第 i 行有皇后14. priva‎t e boole‎a n[] rowEx‎i sts = new boole‎a n[queen‎sNum + 1];15.16.// a[i] = true 表示右高左‎低的第 i 条斜线有皇‎后17. priva‎t e boole‎a n[] a = new boole‎a n[queen‎s Num * 2];18.19.// b[i] = true 表示左高右‎低的第 i 条斜线有皇‎后20. priva‎t e boole‎a n[] b = new boole‎a n[queen‎s Num * 2];21.22.// 初始化变量‎23. priva‎t e void init() {24. for (int i = 0; i < queen‎s Num + 1; i++) {25. rowEx‎i sts[i] = false‎;26. }27.28. for(int i = 0; i < queen‎s Num * 2; i++) {29. a[i] = b[i] = false‎;30. }31. }32.33.// 判断该位置‎是否已经存‎在一个皇后‎,存在则返回‎true34. priva‎t e boole‎a n isExi‎s ts(int row, int col) {35. retur‎n (rowEx‎i sts[row] || a[row + col - 1]|| b[queen‎s Num + col - row]);36. }37.38.// 主方法:测试放置皇‎后39. publi‎c void testi‎n g(int colum‎n) {40.41.// 遍历每一行‎42. for (int row = 1; row < queen‎s Num + 1; row++) {43.// 如果第 row 行第 colum‎n列可以放置‎皇后44. if (!isExi‎s ts(row, colum‎n)) {45.// 设置第 row 行第 colum‎n列有皇后46. queen‎s[colum‎n] = row;47.// 设置以第 row 行第 colum‎n列为交叉点‎的斜线不可‎放置皇后48. rowEx‎i sts[row] = a[row + colum‎n - 1] = b[queen‎s Num + colum‎n - row] = true;49.50.// 全部尝试过‎,打印51. if(colum‎n == queen‎s Num) {52. for(int col = 1; col <= queen‎s Num; col++) {53. Syste‎m.out.print‎("("+col +"," + queen‎s[col] + ") ");54. }55. Syste‎m.out.print‎l n();56. }else {57.// 放置下一列‎的皇后58. testi‎n g(colum‎n + 1);59. }60.// 撤销上一步‎所放置的皇‎后,即回溯61. rowEx‎i sts[row] = a[row + colum‎n - 1] = b[queen‎s Num + colum‎n - row] = false‎;62. }63. }64. }65.66.//测试67. publi‎c stati‎c void main(Strin‎g[] args) {68. N_Que‎e ns queen‎= new N_Que‎e ns();69. queen‎.init();70.// 从第 1 列开始求解‎71. queen‎.testi‎n g(1);72. }73.}六、运行结果当N = 8 时,求解结果如‎下(注:横坐标为列数,纵坐标为行数):(1,1) (2,5) (3,8) (4,6) (5,3) (6,7) (7,2) (8,4)1.(1,1) (2,6) (3,8) (4,3) (5,7) (6,4) (7,2) (8,5)2.(1,1) (2,7) (3,4) (4,6) (5,8) (6,2) (7,5) (8,3)3.... ...4.... ...5.(1,8) (2,2) (3,4) (4,1) (5,7) (6,5) (7,3) (8,6)6.(1,8) (2,2) (3,5) (4,3) (5,1) (6,7) (7,4) (8,6)7.(1,8) (2,3) (3,1) (4,6) (5,2) (6,5) (7,7) (8,4)8.(1,8) (2,4) (3,1) (4,3) (5,6) (6,2) (7,7) (8,5)当N = 4 时,求解结果如‎下:1.(1,2) (2,4) (3,1) (4,3)2.(1,3) (2,1) (3,4) (4,2)七、实验小结:1、根据问题选‎择恰当的数‎据结构非常‎重要,就像上面 a 、b 标志数组来‎表示每一条‎斜线的编号‎顺序以及方‎向都相当重‎要。

N皇后问题—回溯算法经典例题

N皇后问题—回溯算法经典例题

N皇后问题—回溯算法经典例题题⽬描述: N 皇后是回溯算法经典问题之⼀。

问题如下:请在⼀个 n×n 的正⽅形盘⾯上布置 n 名皇后,因为每⼀名皇后都可以⾃上下左右斜⽅向攻击,所以需保证每⼀⾏、每⼀列和每⼀条斜线上都只有⼀名皇后。

题⽬分析: 在 N 皇后问题中,回溯算法思路是每⼀次只布置⼀个皇后,如果盘⾯可⾏,就继续布置下⼀个皇后。

⼀旦盘⾯陷⼊死局,就返回⼀步,调整上⼀个皇后的位置。

重复以上步骤,如果解存在,我们⼀定能够找到它。

可以看到,我们在重复“前进—后退—前进—后退”这⼀过程。

问题是,我们不知道⼀共需要重复这个过程多少次,也不能提前知道 n 是多少,更不知道每⼀次后退时需要后退⼏⾏,因此我们不能利⽤ for 循环和 while 循环来实现这个算法。

因此我们需要利⽤递归来实现代码结构。

逻辑如下:当⽅法布置完当前⾏的皇后,就让⽅法调⽤⾃⼰去布置下⼀⾏的皇后。

当盘⾯变成绝境的时候,就从当前⽅法跳出来,返回到上⼀⾏,换掉上⼀⾏的皇后再继续。

我们定义 NQueens(n) ⽅法,它负责输出所有成⽴的 n×n 盘⾯。

其中 1 代表皇后,0 代表空格。

代码:def NQueens(n): #输出所有成⽴的n·n盘⾯cols = [0 for _ in range(n)] #每⼀⾏皇后的纵坐标res = [] #结果列表def checkBoard(rowIndex): #检查盘⾯是否成⽴,rowIndex是当前⾏数for i in range(rowIndex):if cols[i]==cols[rowIndex]: #检查竖线return Falseif abs(cols[i]-cols[rowIndex]) == rowIndex-i: #检查斜线return Falsereturn Truedef helper(rowIndex): #布置第rowIndex⾏到最后⼀⾏的皇后if rowIndex==n: #边界条件board = [[0 for _ in range(n)] for _ in range(n)]for i in range(n):board[i][cols[i]] = 1res.append(board) #把当前盘⾯加⼊结果列表return#返回for i in range(n): #依次尝试当前⾏的空格cols[rowIndex] = iif checkBoard(rowIndex): #检查当前盘⾯helper(rowIndex+1) #进⼊下⼀⾏helper(0) #从第1⾏开始return resprint(NQueens(4))代码分析: 在 NQueens() ⽅法中,我们会定义 helper(x) ⽅法帮助实现递归结构。

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

HHIT
Algorithm
(1)如果X=(x1, x2, …, xi+1)是问题的最终解,则输出这个解。 如果问题只希望得到一个解,则结束搜索,否则继续搜索其 他解; (2)如果X=(x1, x2, …, xi+1)是问题的部分解,则继续构造解 向量的下一个分量; (3)如果X=(x1, x2, …, xi+1)既不是问题的部分解也不是问题 的最终解,则存在下面两种情况: ① 如果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);
HHIT
算法8.5——n皇后问题
Algorithm
HHIT
Algorithm
HHIT
Algorithm
显然,棋盘的每一行上可以而且必须摆放一个皇后, 所以,n皇后问题的可能解用一个n元向量X=(x1, x2, …, xn) 表示,其中,1≤i≤n并且1≤xi≤n,即第i个皇后放在第i行第 xi列上。 由于两个皇后不能位于同一列上,所以,解向量X必 须满足约束条件: xi≠xj (式8.1)
HHIT
Algorithm 为了简化问题,下面讨论四皇后问题。 四皇后问题的解空间树是一个完全4叉树,树的根结 点表示搜索的初始状态,从根结点到第2层结点对应皇后1 在棋盘中第1行的可能摆放位置,从第2层结点到第3层结 点对应皇后2在棋盘中第2行的可能摆放位置,依此类推。
1 1 2 3 4 图8.11 四皇后问题 2 3 4 皇后1 皇后2 皇后3 皇后4
HHIT
Algorithm
算法8.4:可以放置一个新皇后吗?
Procedure PLACE(k)
//如果一个皇后能放在第k行和X(k)列,则返回true,否则返回 false。X是一个全程数组,进入此过程时已置入了k个值。ABS(r) 过程返回r的绝对值//
global X(1:k); integer i,k; i 1 while i&li)-X(k))=ABS(i-k) then return (false) end if ii+1 判断是否有其它的皇 repeat 后与之在同一列或同 一斜对角线上 return (true) End PLACE
若两个皇后摆放的位置分别是(i, xi)和(j, xj),在棋盘 上斜率为-1的斜线上,满足条件i-j= xj-xi ,在棋盘上斜 率为1的斜线上,满足条件i-j= xi-xj,综合两种情况, 由于两个皇后不能位于同一斜线上,所以,解向量X必须 满足约束条件: |i- j |≠| xi -xj| (式8.2)
HHIT
Algorithm
回溯法求解4皇后问题的搜索过程
Q Q ×× Q Q ×× Q × ××× (c) Q ×××Q Q (g) (h) (i) Q Q ×Q (d) Q Q Q ××Q (j) (e) Q Q Q Q
(a) Q
(b) Q
Q ×Q × ×× ×
(f)
HHIT
Algorithm
HHIT
Algorithm
8.3.1 八皇后问题
八皇后问题是十九世纪著名的数学家高斯于 1850年提出的。问题是:在8×8的棋盘上摆放八 个皇后,使其不能互相攻击,即任意两个皇后都 不能处于同一行、同一列或同一斜线上。可以把 八皇后问题扩展到n皇后问题,即在n×n的棋盘 上摆放n个皇后,使任意两个皇后都不能处于同 一行、同一列或同一斜线上。
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
算法的实现
• 假设回溯法要找出所有的答案结点 。 • 设(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
Algorithm
8.1.3 回溯法的求解过程
由于问题的解向量 X=(x1, x2, …, xn) 中的每个分量 xi(1≤i≤n)都属于一个有限集合Si={ai1, ai2, …, airi},因此, 回溯法可以按某种顺序(例如字典序)依次考察笛卡儿 积S1×S2×…×Sn中的元素。 初始时,令解向量X为空,然后,从根结点出发,选 择S1的第一个元素作为解向量X的第一个分量,即x1= a11, 如果X=(x1)是问题的部分解,则继续扩展解向量X,选择 S2 的第一个元素作为解向量X的第 2个分量,否则,选择 S1的下一个元素作为解向量X的第一个分量,即x1= a12。 依此类推,一般情况下,如果X=(x1, x2, …, xi)是问题的部 分解,则选择Si+1的第一个元素作为解向量X的第i+1个分 量时,有下面三种情况:
相关文档
最新文档