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

n皇后问题实验报告n皇后问题实验报告引言:n皇后问题是一个经典的数学问题,它要求在一个n×n的棋盘上放置n个皇后,使得它们互相之间不能相互攻击,即任意两个皇后不能处于同一行、同一列或同一对角线上。
本实验旨在通过编程实现n皇后问题的求解,并探索不同算法在解决该问题上的性能差异。
实验步骤及结果:1. 回溯算法的实现与性能分析回溯算法是最常见的解决n皇后问题的方法之一。
它通过递归的方式遍历所有可能的解,并通过剪枝操作来提高效率。
我们首先实现了回溯算法,并对不同规模的问题进行了求解。
在测试中,我们将问题规模设置为4、8、12和16。
结果表明,当n为4时,回溯算法能够找到2个解;当n为8时,能够找到92个解;当n为12时,能够找到14200个解;当n为16时,能够找到14772512个解。
可以看出,随着问题规模的增加,回溯算法的求解时间呈指数级增长。
2. 启发式算法的实现与性能分析为了提高求解效率,我们尝试了一种基于启发式算法的解决方法。
在该方法中,我们使用了遗传算法来搜索解空间。
遗传算法是一种模拟生物进化过程的优化算法,通过进化操作(如选择、交叉和变异)来寻找问题的最优解。
我们将遗传算法应用于n皇后问题,并对不同规模的问题进行了求解。
在测试中,我们将问题规模设置为8、12和16。
结果表明,遗传算法能够在较短的时间内找到问题的一个解。
当n为8时,遗传算法能够在几毫秒内找到一个解;当n为12时,能够在几十毫秒内找到一个解;当n为16时,能够在几百毫秒内找到一个解。
相比之下,回溯算法在同样规模的问题上需要几秒钟甚至更长的时间。
3. 算法性能对比与分析通过对比回溯算法和启发式算法的性能,我们可以看到启发式算法在求解n皇后问题上具有明显的优势。
回溯算法的求解时间随问题规模呈指数级增长,而启发式算法的求解时间相对较短。
这是因为启发式算法通过优化搜索策略,能够更快地找到问题的解。
然而,启发式算法并非没有缺点。
N皇后问题java实现

N皇后问题java实现N皇后问题是⼀个典型的约束求解问题,利⽤递归机制,可以很快的得到结果。
N皇后问题的描述:在⼀个n*n的棋盘上,摆放n个皇后,要求每个皇后所在⾏、列、以及两个对⾓线上不能出现其他的皇后,否则这些皇后之间将会相互攻击。
如下图所⽰。
利⽤递归机制,可以很容易的求解n皇后问题。
针对⼋皇后,总共有92种解。
下⾯将给出N-皇后问题的⼀般求解代码,在这⾥代码是使⽤java编码的。
总共设计了三个类,⼀个是皇后类(Queen),⼀个棋盘类(Board),⼀个是求解主程序类(NQueens)。
具体代码如下:1: import java.util.ArrayList;2: import java.util.List;3:4: public class NQueens {5:6: private int numSolutions;//求解结果数量7: private int queenSize;//皇后的多少8: private Board board;//棋盘9: private List<Queen> queens = new ArrayList<Queen>();//皇后10: //private List<Queen> nQueens = new ArrayList<Queen>();11:12: public NQueens(){13:14: }15:16: public NQueens(int size){17: numSolutions = 0;18: queenSize = size;19: board = new Board(queenSize);20: for (int i = 0; i < queenSize; i++) {21: Queen queen = new Queen();22: queens.add(queen);23: }24:25: }26:27: public void solve(){28: System.out.println("Start solve....");29: putQueen(0);30: }31:32: private void putQueen(int index){33:34: int row = index;35: //如果此列可⽤36: for (int col = 0; col < board.getQueenSize(); col++) {37: if (board.getLayout()[row][col] == 0) {38: //将皇后的位置置为此列位置39: queens.get(row).setPosition(col);40: //然后将相应的位置(此列的正下⽅以及两个对⾓线)加1(即使其不可⽤) 41: for (int i = row + 1; i < board.getQueenSize(); i++) {42: board.getLayout()[i][col] ++;43: if (row + col - i >= 0) {44: board.getLayout()[i][row + col - i] ++;45: }46: if (i + col - row < board.getQueenSize()) {47: board.getLayout()[i][i + col - row] ++;48: }49: }50:51: if (row == board.getQueenSize()-1) {52: numSolutions++;53: printSolution(numSolutions);54: }else {55: putQueen(row + 1);56: }57: //回溯,将相应的位置(此列的正下⽅以及两个对⾓线)减158: for (int i = row + 1; i < board.getQueenSize(); i++) {59: board.getLayout()[i][col] --;60: if (row + col - i >= 0) {61: board.getLayout()[i][row + col - i] --;62: }63: if (i + col - row < board.getQueenSize()) {64: board.getLayout()[i][i + col - row] --;65: }66: }67:68: }69: }70: }71: //打印求解结果72: private void printSolution(int i){73: System.out.println("The "+i+ " solution is:");74: for (int j = 0; j < board.getQueenSize(); j++) {75: for (int k = 0; k < board.getQueenSize(); k++) {76: System.out.print(queens.get(j).getPosition() == k ? " * ":" - ");77: }78: System.out.println();79: }80: System.out.println();81: }82: /**83: * @param args84: */85: public static void main(String[] args) {86: //可以改变参数87: NQueens nQueens = new NQueens(8);88: nQueens.solve();89:90: }91:92:93:94: }95: import java.io.Serializable;96:97: //皇后类98: public class Queen implements Serializable, Cloneable {99:100: /**101: *102: */103: private static final long serialVersionUID = 7354459222300557644L; 104: //皇后的位置105: private int position;106:107: public Queen(){108:109: }110:111: public int getPosition() {112: return position;113: }114:115: public void setPosition(int position) {116: this.position = position;117: }118:119: public Object clone() throws CloneNotSupportedException {120:121: return super.clone();122: }123: }124:125: import java.io.Serializable;126:127: //棋盘类128: public class Board implements Serializable,Cloneable {129:130: /**131: *132: */133: private static final long serialVersionUID = -2530321259544461828L; 134:135: //棋盘的⼤⼩136: private int queenSize;137:138: //棋盘的布局139: private int[][] layout;140:141: public Board(int size){142: this.queenSize = size;143: yout = new int[queenSize][queenSize];144: //初始化,使棋盘所有位置都可⽤,即全部置为0145: for (int i = 0; i < queenSize; i++) {146: for (int j = 0; j < queenSize; j++) {147: layout[i][j] = 0;148:149: }150: }151: }152:153: public int getQueenSize() {154: return queenSize;155: }156:157: public void setQueenSize(int queenSize) {158: this.queenSize = queenSize;159: }160:161: public int[][] getLayout() {162: return layout;163: }164:165: public void setLayout(int[][] layout) {166: yout = layout;167: }168:169: public Object clone() throws CloneNotSupportedException { 170:171: return super.clone();172: }173:174: }175:。
回溯法实验(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; //返回不同解的个数}。
回溯法解皇后问题

Ch1-绪论1. 回溯法解皇后问题#include "stdio.h"#include "math.h"#include "stdlib.h"void queen(int n){ int i,j,k,jt,*q;q=malloc(n*sizeof(int));for(i=0; i<n; i++) q[i]=0;i=0; jt=1;printf("\n");printf("%d queen problem\n",n);while(jt==1){ if (q[i]<n){ k=0;while((k<i)&&((q[k]-q[i])*(fabs(q[k]-q[i])-fabs(k-i)))!=0) k=k+1;if (k<i) q[i]=q[i]+1;else{ if (i==n-1){ for(j=0; j<n; j++)printf("%5d",q[j]+1);printf("\n");q[n-1]=q[n-1]+1;}else i=i+1;}}else{ q[i]=0; i=i-1;if (i<0){ printf("\n"); free(q); return; }q[i]=q[i]+1;}}}2. 简单二分法求方程实根(1)#include "stdio.h"#include "math.h"double root(a,b,eps,f)double a,b,eps,(*f)();{ double f0,f1,c;f0=(*f)(a);while (fabs(a-b)>=eps){ c=(a+b)/2; f1=(*f)(c);if (f1==0) return(c);if (f0*f1>0) a=c;else b=c;}c=(a+b)/2;return(c);}(2)#include "root.c"main(){ double a,b,eps,f();a=1; b=2; eps=0.000001;printf("x=%7.3f\n",root(a,b,eps,f));}double f(x)double x;{ double y;y=x+log(x)-2.2;return(y);}Ch2-矩阵与线性代数方程组(1)文件头:#include "math.h"#include "stdio.h"int maqr(m,n,a,q)int m,n;double a[],q[];{ int i,j,k,l,nn,p,jj;double u,alpha,w,t;if (m<n){ printf("fail\n"); return(0);}for (i=0; i<=m-1; i++)for (j=0; j<=m-1; j++){ l=i*m+j; q[l]=0.0;if (i==j) q[l]=1.0;}nn=n;if (m==n) nn=m-1;for (k=0; k<=nn-1; k++){ u=0.0; l=k*n+k;for (i=k; i<=m-1; i++){ w=fabs(a[i*n+k]);if (w>u) u=w;}alpha=0.0;for (i=k; i<=m-1; i++){ t=a[i*n+k]/u; alpha=alpha+t*t;}if (a[l]>0.0) u=-u;alpha=u*sqrt(alpha);if (fabs(alpha)+1.0==1.0){ printf("fail\n"); return(0);}u=sqrt(2.0*alpha*(alpha-a[l]));if ((u+1.0)!=1.0){ a[l]=(a[l]-alpha)/u;for (i=k+1; i<=m-1; i++){ p=i*n+k; a[p]=a[p]/u;}for (j=0; j<=m-1; j++){ t=0.0;文件尾:for (jj=k; jj<=m-1; jj++)t=t+a[jj*n+k]*q[jj*m+j];for (i=k; i<=m-1; i++){ p=i*m+j; q[p]=q[p]-2.0*t*a[i*n+k];}}for (j=k+1; j<=n-1; j++){ t=0.0;for (jj=k; jj<=m-1; jj++)t=t+a[jj*n+k]*a[jj*n+j];for (i=k; i<=m-1; i++){ p=i*n+j; a[p]=a[p]-2.0*t*a[i*n+k];}}a[l]=alpha;for (i=k+1; i<=m-1; i++)a[i*n+k]=0.0;}}for (i=0; i<=m-2; i++)for (j=i+1; j<=m-1;j++){ p=i*m+j; l=j*m+i;t=q[p]; q[p]=q[l]; q[l]=t;}return(1);}(2)#include "stdio.h"#include "maqr.c"main(){ int i,j;static double q[4][4],a[4][3]={ {1.0,1.0,-1.0}, {2.0,1.0,0.0},{1.0,-1.0,0.0},{-1.0,2.0,1.0}};i=maqr(4,3,a,q);if (i!=0){ printf("MAT Q IS:\n");for (i=0; i<=3; i++){ for (j=0; j<=3; j++)printf("%13.7e ",q[i][j]);printf("\n");}printf("\n");printf("MAT R IS:\n");for (i=0; i<=3; i++){ for (j=0; j<=2; j++)printf("%13.7e ",a[i][j]);printf("\n");}printf("\n");}}(3)文件头:#include "stdlib.h"#include "math.h"int muav(m,n,a,u,v,eps,ka)int m,n,ka;double eps,a[],u[],v[];{ int i,j,k,l,it,ll,kk,ix,iy,mm,nn,iz,m1,ks;double d,dd,t,sm,sm1,em1,sk,ek,b,c,shh,fg[2],cs[2]; double *s,*e,*w;void ppp();void sss();s=malloc(ka*sizeof(double));e=malloc(ka*sizeof(double));w=malloc(ka*sizeof(double));it=60; k=n;if (m-1<n) k=m-1;l=m;if (n-2<m) l=n-2;if (l<0) l=0;ll=k;if (l>k) ll=l;if (ll>=1){ for (kk=1; kk<=ll; kk++){ if (kk<=k){ d=0.0;for (i=kk; i<=m; i++){ ix=(i-1)*n+kk-1; d=d+a[ix]*a[ix];}s[kk-1]=sqrt(d);if (s[kk-1]!=0.0){ ix=(kk-1)*n+kk-1;if (a[ix]!=0.0){ s[kk-1]=fabs(s[kk-1]);if (a[ix]<0.0) s[kk-1]=-s[kk-1];}for (i=文件尾:{ int i,j,p,q;double d;if (m>=n) i=n;else i=m;for (j=1; j<=i-1; j++){ a[(j-1)*n+j-1]=s[j-1];a[(j-1)*n+j]=e[j-1];}a[(i-1)*n+i-1]=s[i-1];if (m<n) a[(i-1)*n+i]=e[i-1];for (i=1; i<=n-1; i++)for (j=i+1; j<=n; j++){ p=(i-1)*n+j-1; q=(j-1)*n+i-1;d=v[p]; v[p]=v[q]; v[q]=d;}return;}static void sss(fg,cs)double cs[2],fg[2];{ double r,d;if ((fabs(fg[0])+fabs(fg[1]))==0.0){ cs[0]=1.0; cs[1]=0.0; d=0.0;} else{ d=sqrt(fg[0]*fg[0]+fg[1]*fg[1]);if (fabs(fg[0])>fabs(fg[1])){ d=fabs(d);if (fg[0]<0.0) d=-d;}if (fabs(fg[1])>=fabs(fg[0])){ d=fabs(d);if (fg[1]<0.0) d=-d;}cs[0]=fg[0]/d; cs[1]=fg[1]/d;}r=1.0;if (fabs(fg[0])>fabs(fg[1])) r=cs[1];elseif (cs[0]!=0.0) r=1.0/cs[0];fg[0]=d; fg[1]=r;return;}#include "stdio.h"#include "cgauss.c"main(){ int i;static double ar[4][4]={ {1.0,3.0,2.0,13.0},{7.0,2.0,1.0,-2.0},{9.0,15.0,3.0,-2.0},{-2.0,-2.0,11.0,5.0}};static double ai[4][4]={ {3.0,-2.0,1.0,6.0},{-2.0,7.0,5.0,8.0},{9.0,-3.0,15.0,1.0},{-2.0,-2.0,7.0,6.0}}; static double br[4]={2.0,7.0,3.0,9.0};static double bi[4]={1.0,2.0,-2.0,3.0};if (cgauss(4,ar,ai,br,bi)!=0)for (i=0;i<=3;i++)printf("b(%d)=%13.7e +j %13.7e\n",i,br[i],bi[i]); }。
回溯法解决N后问题

实验名称
随机与回溯结合解决八皇后问题
课程名称
算法分析与设计
姓名
***
专业班级学号
计算机08-1班
20080701****
日期
2011.06.15
地点
西一楼207
成绩
教师
***,***
一、
1.掌握回溯法的设计思想。
2.设计回溯算法完成N后问题求解。
3.考察回溯法求解问题的有效程度。
二、
}
}
}
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;
return X.sum;
}
for(j=1;j<=8;j++) /*第i)/*即相应的三个数组的对应元素值为1*/
{占用位置(i,j)/*置相应的三个数组对应的元素值为0*/
if i<8
为i+1个皇后选择合适的位置;
else输出一个解
}
四、
皇后个数为1:
皇后个数为2:
皇后个数为3:
皇后个数为4:
皇后个数为8时:
五、
本实验是在别人代码的基础上,结合自己对回溯算法的理解改的。在最初的时候,遇到了很大的麻烦,毕竟是改的别人的代码,对代码的理解不是很透彻,出现了很多错误,但在同学们的帮助下,通过自己的一个下午加一个晚上的修改,终于调试成功。
N皇后问题——JAVA实现(源代码)

2011/2012学年第2学期“算法分析与设计”上机报告目录1. 问题描述 (3)2. 算法分析 (3)3. 伪代码 (4)4. 演示程序设计 (5)5. 演示界面 (5)6. 算法实现 (8)7. 总结 (19)8. 参考文献 (20)1.问题描述:N皇后问题(n-queen problem)是一个经典的组合优化问题,也是一个使用回溯法(backtracking)的典型例子。
回溯法是一种系统地搜索问题解的方法。
为了实现回溯,首先需要为为问题定义一个解空间(solution space),其至少包含问题的一个解(可能是最优解)。
我们要从中找出满足问题约束条件的解,即可行解(feasible solution)。
回溯算法一次扩展一个解,在对部分解进行扩展后,检查到目前为止的解是否为问题的一个解,如果是,则输出;否则,检查是否可以继续扩展。
如果可以,则继续扩展;否则,删除最后添加的元素,尝试当前位置是否有另一元素。
若没有合法的扩展方式,则进行回溯(backtrack)。
N皇后问题要求在一个n×n的棋盘上放置n个皇后,且使得每两个皇后之间都不能相互攻击,即它们中的任意两个都不能位于同一行、同一列或者同一对角线上。
这次的任务就是借助GUI实现N皇后问题的动态演示。
我们设定皇后个数在四个到八个之间可选,所选编程语言为JA V A。
2.算法分析:N皇后问题是回溯法的一个经典例子,它的要求就是要找出在n×n的棋盘上放置n个皇后并使其不能相互攻击的所有解。
设X=(x1,x2,…,xn)表示问题的解,,其中xi表示第i皇后放在第i行所在的列数。
由于不存在两个皇后位于同一列上,因此xi互不相同。
设有两个皇后分别位于棋盘( i , j )和( k , l )处,如果两个皇后位于同一对角线上,则表明它们所在的位置应该满足:i – j = k – l或i + j = k + l。
综合这两个等式可得,如果两个皇后位于同一对角线上,那么它们的位置关系一定满足| j – l | = | i – k |。
n皇后 实验报告

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

Tree-回溯法求N皇后问题#include <stdio.h>#include <malloc.h>#define N 4 //N皇后typedef int Chessboard[N + 1][N + 1]; //第0号位置不用bool check(Chessboard cb, int i, int j) { //看棋盘cb是否满足合法布局int h, k;int m = i + j, n = i - j;for(h=1; h<i; h++) {if(cb[h][j] == 1 && h != i) return false; //检查第j列if(m-h<=N && cb[h][m-h] == 1 && h != i) return false; //检查斜的,m-h<=N是为了保证不越界if(h-n<=N && cb[h][h-n] == 1 && h != i) return false; //检查斜的,h-n<=N是为了保证不越界}for(k=1; k<N; k++)/*检查第i行的*/ if(cb[i][k] == 1 && k != j) return false;return true;}void printfChessboard(Chessboard cb) {//打印棋盘int i, j;for(i=1; i<=N; i++) {for(j=1; j<=N; j++) printf("%d ", cb[i][j]);printf("\n");}printf("\n");}/*进入本函数时,在n*n棋盘前n-1行已放置了互不攻击的i-1个棋子。
现从第i行起继续为后续棋子选择合适位置。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验报告
一、实验名称:回溯法求解N皇后问题(Java实现)
二、学习知识:
回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并以此慢慢地扩大问题规模,迭代地逼近最终问题的解。
这种迭代类似于穷举并且是试探性的,因为当目前的可能答案被测试出不可能可以获得最终解时,则撤销当前的这一步求解过程,回溯到上一步寻找其他求解路径。
为了能够撤销当前的求解过程,必须保存上一步以来的求解路径,这一点相当重要。
三、问题描述
N皇后问题:在一个 N * N 的国际象棋棋盘中,怎样放置 N 个皇后才能使 N 个皇后之间不会互相有威胁而共同存在于棋局中,即在 N * N 个格子的棋盘中没有任何两个皇后是在同一行、同一列、同一斜线上。
深度优先遍历的典型案例。
四、求解思路
1、求解思路:最容易想到的方法就是有序地从第 1 列的第 1 行开始,尝试放上一个皇后,然后再尝试第 2 列的第几行能够放上一个皇后,如果第 2 列也放置成功,那么就继续放置第 3 列,如果此时第 3 列没有一行可以放置一个皇后,说明目前为止的尝试是无效的(即不可能得到最终解),那么此时就应该回溯到上一步(即第 2 步),将上一步(第 2 步)所放置的皇后的位置再重新取走放在另一个符合要求的地方…如此尝试性地遍历加上回溯,就可以慢慢地逼近最终解了。
2、需要解决的问题:如何表示一个 N * N 方格棋盘能够更有效?怎样测试当前所走的试探路径是否符合要求?这两个问题都需要考虑到使用怎样的数据结构,使用恰当的数据结构有利于简化编程求解问题的难度。
3、我们使用以下的数据结构:
int column[col] = row 表示第 col 列的第row 行放置一个皇后
boolean rowExists[i] = true 表示第 i 行有皇后
boolean a[i] = true 表示右高左低的第 i 条斜线有皇后(按→↓顺序从1~ 2*N -1 依次编号)
boolean b[i] = true 表示左高右低的第 i 条斜线有皇后(按→↑顺序从1~ 2*N -1 依次编号)
五、算法实现
对应这个数据结构的算法实现如下:
1.**
2. * 回溯法求解 N 皇后问题
3. * @author haolloyin
4. */
5.public class N_Queens {
6.
7.// 皇后的个数
8. private int queensNum = 4;
9.
10.// column[i] = j表示第 i 列的第 j 行放置一个皇后
11. private int[] queens = new int[queensNum + 1];
12.
13.// rowExists[i] = true 表示第 i 行有皇后
14. private boolean[] rowExists = new boolean[queen
sNum + 1];
15.
16.// a[i] = true 表示右高左低的第 i 条斜线有皇后
17. private boolean[] a = new boolean[queensNum * 2
];
18.
19.// b[i] = true 表示左高右低的第 i 条斜线有皇后
20. private boolean[] b = new boolean[queensNum * 2
];
21.
22.// 初始化变量
23. private void init() {
24. for (int i = 0; i < queensNum + 1; i++) {
25. rowExists[i] = false;
26. }
27.
28. for(int i = 0; i < queensNum * 2; i++) {
29. a[i] = b[i] = false;
30. }
31. }
32.
33.// 判断该位置是否已经存在一个皇后,存在则返回 true
34. private boolean isExists(int row, int col) {
35. return (rowExists[row] || a[row + col - 1]
|| b[queensNum + col - row]);
36. }
37.
38.// 主方法:测试放置皇后
39. public void testing(int column) {
40.
41.// 遍历每一行
42. for (int row = 1; row < queensNum + 1; row+
+) {
43.// 如果第 row 行第 column 列可以放置皇后
44. if (!isExists(row, column)) {
45.// 设置第 row 行第 column 列有皇后
46. queens[column] = row;
47.// 设置以第 row 行第 column 列为交叉点
的斜线不可放置皇后
48. rowExists[row] = a[row + column - 1
] = b[queensNum + column - row] = true;
49.
50.// 全部尝试过,打印
51. if(column == queensNum) {
52. for(int col = 1; col <= queensN
um; col++) {
53. System.out.print("("+col +
"," + queens[col] + ") ");
54. }
55. System.out.println();
56. }else {
57.// 放置下一列的皇后
58. testing(column + 1);
59. }
60.// 撤销上一步所放置的皇后,即回溯
61. rowExists[row] = a[row + column - 1
] = b[queensNum + column - row] = false;
62. }
63. }
64. }
65.
66.//测试
67. public static void main(String[] args) {
68. N_Queens queen = new N_Queens();
69. queen.init();
70.// 从第 1 列开始求解
71. queen.testing(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 标志数组来表示每一条斜线的编号顺序以及方向都相当重要。
看书的时候也是费了些时间来理解的,呼…另外,queens [col] = row 数组只是用了一维而不是二维来表示纵横交错的方格棋盘上特定位置是否有皇后也是比较经济而有意思的。
2、正确运用、组织所确定的数据结构到算法的实现逻辑中也是很重要的,就像代码中的 isExists(int row, int col) 方法内的 (rowExists[row] || a[row + col - 1] || b[queensNum + col - row]) 就是很明确的理解了尝试放置皇后的位置的 x ,y 坐标与斜线之间的数值关系,才使得算法得以正确执行。
当然,对于斜线的编号、顺序也是相当重要的。