zoukankan      html  css  js  c++  java
  • Java编程思想—八皇后问题(数组法、堆栈法)

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

    实验目的:

    (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 皇后的规则,可以设想为数组上同一直线,横线,斜线的数字都不能相同,由此可以得出判断条件;
    ③ 根据判断条件之后,建立回溯点,即可解决问题。

    堆栈法:

    ① 检索当前行是否可以放置一个皇后;
    ② 利用检索过程,通过递归的方式,来确定每个皇后的位置———回溯的思想。

    算法伪代码:

    堆栈法
    数组法

    实验结果:

    递归法1
    递归法2
    递归法3
    最终结果

    实验代码:

    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");
    	}
    
    }
    
    

    出现的问题:

    问题一:条件检查

    实验时,排序的结果出现问题,斜线的情况不能检查出来。
    问题1
    于是我仔细检查了判断部分的代码,发现是变量的重复使用,导致无法正常判断。i和j的值被重复使用。我通过临时变量进行存储,在上一次使用后进行重新赋值,解决了问题,如红圈。
    解决1

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

    一开始不知道递归方法,想用情况分析,循环查找,但是发现在查找时,只能查找到第一个棋子在1,1位置的情况。
    问题2.1
    我猜测是跳出循环的判断出了问题,于是在我的检查下,发现下图第一个圈是要跳出循环,结束整个查找,由于是双重循环,所以我直接在第二个圈设置如果i= =0,则结束循环,共两次跳出。
    问题2.2
    但是我忽略了限制条件,即在i = = 0的时候,并不是都是要结束的,只有圈1的那一种情况才跳出,所以我设置flag变量进行传递,纠正了程序的错误。 更正的代码 见 实验代码 部分。

    实验心得:

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

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

  • 相关阅读:
    Nmap手册
    练习题,新建数据库anyun
    Centos6.5
    七丶人生苦短,我用python【第七篇】
    六丶人生苦短,我用python【第六篇】
    五、人生苦短,我用python【第五篇】
    linux 创建连接命令 ln -s 软连接
    激活phpstorm10.0.1
    php 数组合并方法
    通过jquery来实现文本框和下拉框动态添加效果,能根据自己的需求来自定义最多允许添加数量,实用的jquery动态添加文本框特效
  • 原文地址:https://www.cnblogs.com/Comet-Fei/p/11885211.html
Copyright © 2011-2022 走看看