实验题目:回溯法实验(八皇后问题)

实验目的:

(1) 掌握回溯法求解问题的思想

(2) 学会利用其原理求解相关问题

实验要求:

	使用贪心法求出给定图各点的最短路径,并计算算法的执行时间,分析算法的有效性。利用回溯法解决八皇后问题,检验结果,并计算算法的执行时间,分析算法的有效性。
测试数据可以通过手工寻找三组满足需要的值,测试数组(M,N),其中 M 代表皇后所在的行,N 代表皇后所在的列。

例如,第一组测试数据:

(1,4)、(2,7)、(3,3)、(4、8)、(5,2)、(6,5)、(7,1)、 (8,6);

第二组测试数据

(1,5)、(2,2)、(3,4)、(4,7)、(5,3)、(6,8)、(7,6)、 (8,1);

第三组测试数据

(1,4)、(2,2)、(3,7)、(4,3)、(5,6)、(6,8)、(7,5)、 (8,1)。

最后与编程求得的结果进行比较。如果这三组数据在最后编程求得的结果中,说明程序的编写基本没有什么问题。

实验内容:

(1)问题描述

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后。为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

(2)实验步骤:

数组法:

① 根据 8 皇后问题,可以把其设想为一个数组;

② 根据 8 皇后的规则,可以设想为数组上同一直线,横线,斜线的数字都不能相同,由此可以得出判断条件;

③ 根据判断条件之后,建立回溯点,即可解决问题。

堆栈法:

① 检索当前行是否可以放置一个皇后;

② 利用检索过程,通过递归的方式,来确定每个皇后的位置———回溯的思想。

算法伪代码:



实验结果:







实验代码:

public class EightQueensOfBacktracking {
int count = 0;
//棋盘初始化 清空操作
void initialChessBoard(int chessBoard[][]){
for(int i = 0; i < 8 ; i++){
for(int j = 0; j < 8; j++){
chessBoard[i][j] = 0;
}
}
}
//打印皇后位置
void showLocation(int chessBoard[][]){
System.out.println("————————————");
System.out.println("皇后的坐标为:");
for(int i = 0; i < 8 ; i++){
for(int j = 0; j < 8; j++){
if(chessBoard[i][j] != 0){
System.out.print(" ( " + (i+1) + " , " + (j+1) + " ) ");
}
}
}
System.out.println();
System.out.println("棋盘如下:");
for(int i = 0; i < 8 ; i++){
for(int j = 0; j < 8; j++){
System.out.print(chessBoard[i][j] + " ");
}
System.out.println();
}
}
//行列检查 斜线检查
boolean checkAll(int i, int j, int chessBoard[][]){
int tempI = i;
int tempJ = j;
if((i>7)||(j>7)) return false;
//check column
for(int k = 0; k <= j; k++){
if(chessBoard[i][k] != 0){
return false;
}
}
//check row
for(int k = 0; k <= i; k++){
if(chessBoard[k][j] != 0){
return false;
}
}
//左上斜线检查
while(true){
if(chessBoard[i][j] != 0)
return false;
if((i == 0)||(j == 0)) break;
i--;
j--;
}
//右上斜线检查
i = tempI;
j = tempJ;
while(true){
if(chessBoard[i][j] != 0)
return false;
if((i == 0)||(j == 7)) break;
i--;
j++;
}
return true;
} //堆栈方法
public void findQueen(int i, int chessBoard[][], EightQueensOfBacktracking eightQueens){
if(i>7){
eightQueens.count++;
eightQueens.showLocation(chessBoard);
}
//回溯法
boolean judge;
for(int m = 0; m<8; m++){
judge = eightQueens.checkAll(i, m, chessBoard);
if(judge){
chessBoard[i][m] = 1;
eightQueens.findQueen(i+1, chessBoard, eightQueens);
chessBoard[i][m] = 0;
}
}
} public static void main(String[] args) {
// TODO Auto-generated method stub
//函数调用
EightQueensOfBacktracking eightQueens = new EightQueensOfBacktracking();
//摆法的数量
int count = 0;
int k = 0;//临时变量
int i = 0, j= 0;//i 行 ;j 列
boolean judge = true;//检查结果
//8个皇后,1-8表示
int queens = 1;
//棋盘 8*8
int chessBoard[][] = new int [8][8];
//每次找到解后清盘,即初始化
eightQueens.initialChessBoard(chessBoard); long startTime2 = System.nanoTime();
//堆栈方法:
eightQueens.findQueen(0, chessBoard, eightQueens);
long endTime2 = System.nanoTime(); //数组方法
//失败1:在当前行非末尾:列+1;
//失败2:在当前行的末尾:判断行,如果是0行,则所有情况已遍历,结束;
// 如果不是,回到上一行,遍历,查找到上一行的棋子,记录位置,找到下一位置,则结束查找上一个位置;
// 如果上一行也遍历完,列的位置为7,上一个位置处没有下一位置,则再找上上一行的位置,进行上面的循环;
// 如果上一行是0行,则也结束遍历,flag为0,结束上一位置的查找; //成功:标记位置,如果位置标记是8,则为一种方案,输出,方法计数加一,标记减1,queens表示下一个要放的棋子;
// 行加1,列归0,通过失败1和失败2再找到上一个棋子的的位置
long startTime1 = System.nanoTime();
int flag = 1;
while(true){
judge = eightQueens.checkAll(i, j, chessBoard);
//这一行未检查完,检查这一行下一个位置
if((judge == false)&&(j != 7)){
j++;
continue;
}
else if((judge == false)&&(j == 7)){
if(i == 0) break;//表示所有的情况已经遍历,结束循环
i--;//回到上一行
k = 0;//查找,用来遍历
while(true){
if(chessBoard[i][k] != 0){//找到上一行棋子的位置
queens = chessBoard[i][k];//表示这个棋子放到棋盒中
chessBoard[i][k] = 0;//把棋子取走
j = k + 1;//找到下一个位置,准备下一次循环
if(j > 7){//如果上一行也遍历完,找到上上一行
if(i == 0){
flag = 0;
break;//如果上一行是0,那么没有上上一行,结束
}
i--;
k =0;
continue;//重新开始查找棋子
}
break;
}
k++;
}
if(flag == 0){
break;
}
continue;
}
//检查通过,放下棋子,到下一行
chessBoard[i][j] = queens;
if(queens == 8){//找到一种方法
eightQueens.showLocation(chessBoard);
count++;
queens--;
//输出后,假装这个摆法不行,继续查找
}
queens++;//queens的值表示下一个要放的棋子
i++;
j = 0; }
long endTime1 = System.nanoTime();
System.out.println("数组方法共有" + count + "种摆法");
System.out.println("堆栈递归方法共有" + eightQueens.count + "种摆法");
System.out.println("数组程序运行时间:" + (endTime1 - startTime1) + "ns");
System.out.println("堆栈递归程序运行时间:" + (endTime2 - startTime2) + "ns");
} }

出现的问题:

问题一:条件检查

实验时,排序的结果出现问题,斜线的情况不能检查出来。



于是我仔细检查了判断部分的代码,发现是变量的重复使用,导致无法正常判断。i和j的值被重复使用。我通过临时变量进行存储,在上一次使用后进行重新赋值,解决了问题,如红圈。

问题二:数组法 跳出循环情况分析

一开始不知道递归方法,想用情况分析,循环查找,但是发现在查找时,只能查找到第一个棋子在1,1位置的情况。



我猜测是跳出循环的判断出了问题,于是在我的检查下,发现下图第一个圈是要跳出循环,结束整个查找,由于是双重循环,所以我直接在第二个圈设置如果i= =0,则结束循环,共两次跳出。



但是我忽略了限制条件,即在i = = 0的时候,并不是都是要结束的,只有圈1的那一种情况才跳出,所以我设置flag变量进行传递,纠正了程序的错误。 更正的代码 见 实验代码 部分。

实验心得:

本次实验体现的是回溯法。经过本次实验,发现自己对回溯的理解并不全面,不会应用。初次做这个题,想的只是遍历,在循环中,进行人工的回溯。后来发现回溯时的情况分析十分复杂,并不能很好的发现并且处理所有情况,只求出4种结果。经过学习后了解到,对于回溯,本题不需要自己考虑情况,只需给出限制条件进行筛选,在满足条件的情况下进行重复调用自身函数,在完成函数后,进行自身位置的值的清空,为之后回溯进行准备即可。让我明白递归是回溯的一种很好的实现方式。

在使用java的过程中,为了解决遇到的问题还进行了调试,让我对debug和调试有了进一步的掌握。

说明:递归法是调用系统堆栈进行操作,所以属于堆栈法。

递归堆栈方法可以参考链接:八皇后递归堆栈方法

Java编程思想—八皇后问题(数组法、堆栈法)的更多相关文章

  1. Java编程思想学习(十二) 数组和容器

    一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的 ...

  2. Java编程思想里的泛型实现一个堆栈类

    觉得作者写得太好了,不得不收藏一下. 对这个例子的理解: //类型参数不能用基本类型,T和U其实是同一类型. //每次放新数据都成为新的top,把原来的top往下压一级,通过指针建立链接. //末端哨 ...

  3. Java编程思想学习(八) 内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...

  4. Java编程思想(11~17)

    [注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第十一章 持有对象 11.1 泛型和类型安全的容器>eg: List<St ...

  5. Java编程思想 (1~10)

    [注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第一章 对象导论 1.万物皆对象2.程序就是对象的集合3.每个对象都是由其它对象所构成 ...

  6. 《Java编程思想第四版》附录 B 对比 C++和 Java

    <Java编程思想第四版完整中文高清版.pdf>-笔记 附录 B 对比 C++和 Java “作为一名 C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且 Java 的语法无疑是 ...

  7. Java编程思想总结笔记Chapter 5

    初始化和清理是涉及安全的两个问题.本章简单的介绍“垃圾回收器”及初始化知识. 第五章  初始化与清理 目录:5.1 用构造器确保初始化5.2 方法重载5.3 默认构造器5.4 this关键字5.5 清 ...

  8. Java编程思想——第17章 容器深入研究(two)

    六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add ...

  9. JAVA编程思想——分析阅读

    需要源码.JDK1.6 .编码风格参考阿里java规约 7/12开始 有点意识到自己喜欢理论大而泛的模糊知识的学习,而不喜欢实践和细节的打磨,是因为粗心浮躁导致的么? cron表达式使用 设计能力.领 ...

随机推荐

  1. 集合(四) Hashtable

    2.Hashtable Hashtable,顾名思义,哈希表,本来是已经被淘汰的内容,但在某一版本的Java将其实现了Map接口,因此也成为常用的集合类,但是hashtable由于和hashmap十分 ...

  2. CodeForces - 1209F Koala and Notebook(拆边+BFS)

    题意:给定一个n个点m条边的无向图,边权分别为1-m,从起点1出发,每经过一条边就把边权以字符串的形式加入末尾,求到达其他每个点的最小字符串(长度不同的短的更小,否则字典序小的更小). 思路很巧妙,将 ...

  3. 在哪里查看java的jar包版本?

    jar包根目录里的META-INF目录下的MANIFEST.MF文件里一般有会记录版本信息,可以到这个文件里查看 .

  4. Java 工厂方法模式的简单示例

    工厂方法模式:也叫工厂模式,属于类创建型模式,工厂父类(接口)负责定义产品对象的公共接口,而子类工厂则负责创建具体的产品对象. 目的:是为了把产品的实例化操作延迟到子类工厂中完成,通过工厂子类来决定究 ...

  5. Hive中的日志

    Hive中的日志分为两种1. 系统日志,记录了hive的运行情况,错误状况.2. Job 日志,记录了Hive 中job的执行的历史过程. 1.系统日志 系统日志存储在什么地方呢 ?在hive/con ...

  6. fedora29 安装mongodb 4.0,6问题记录

    如果运行mongod命令时提示 无加载共享库libcrypto.so.10,那就到页面下载http://www.rpmfind.net/linux/rpm2html/search.php?query= ...

  7. 安装Go语言及环境的搭建

    下载 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://golang.google.cn/dl/ 安装 Windows安装 此安装实例以 ...

  8. django admin登陆页出现TypeError at /admin/

    出现此错误的原因主要是,添加多条URL时urlpatterns后面的序列符号写错了,检查所有的urls.py文件将{}改为[]. error: urlpatterns = {} right: urlp ...

  9. 【leetcode】1257. Smallest Common Region

    题目如下: You are given some lists of regions where the first region of each list includes all other reg ...

  10. QT Creator有中文出现“常量中有换行符 ”的解决办法

    QT Creator有中文出现“常量中有换行符 ”的解决办法 QT Creator在QT5.9下报错“常量中有换行符 ”,我的代码中有中文,而且在Windows 10下用微软VS编译器编译.造成这个报 ...