背景及问题介绍:
八皇后问题(英文:Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。
解决方法:创建一个8x8二维数组解决或者一个一维数组解决,对于一维数组arr,用下标i表示行数,对应的arr[i]表示列数。
总体步骤:
步骤一:先在第一行的第一个位置放置皇后
步骤二:确定好第一行的皇后之后,从第二行的第一个到第八个位置,挨个判断,如果满足条件,确定好一个皇后,再去找第三行,如果八个位置都不满足情况,那么就回到第一行,把皇后放置第二个位置,又继续看第二行。
步骤三:直到第八行,八个位置挨个判断,如果符合条件,就直接打印数组,当第八行都判断完之后,将第八行回到第七行,从之前的皇后的下一个位置开始判断,满足条件,又去看第八行。当第七行所有位置都判断完之后,回溯上第六行,以此类推,直到把第一行八个位置轮完。
要实现在这个递归和回溯的功能,可用以下代码实现:
二维数组:
class BHH {
public void game(int board[][], int row) {//row记录行数,row = 0
//遍历一行中的每一列
for (int i = 0; i < 8; i++) {
if (checkBoard(board, row, i))//检测该位置是否正确,row,i为该位置的行数和列数
{
board[row][i] = 1;//将该位置设置为皇后
if (row == 7) {
print(board);//直到在第八行能找到满足要求的位置才打印
} else {
game(board, row + 1);
}
}
board[row][i] = 0;//将皇后设置为0,防止干扰打印下一个棋盘的结果
}
}
一维数组:
class BHH {
private int n = 0;//记录结果个数
public void game(int board[], int row) {//row记录行数,row = 0
//遍历一行中的每一列
for (int i = 0; i < 8; i++) {
board[row] = i;//将该位置设置为皇后
if (checkBoard(board, row, board[row])) {//检测该位置是否正确,row,board[row]为该位置的行数和列数
if (row == 7) {
print(board,n);
} else
game(board, row + 1);
}
//board[row] = 0;一维数组是不需要这一步的,当循环下一次时,board[row] = i会覆盖上一次的结果
}
}
而检测每一个要放置的皇后位置是否合理。对于这一步骤,检测是否同列就不用说了,对于检测即将放置的皇后的左上和右上是否有皇后,我们把行列看成x,y轴(谁是x谁是y无所谓),这相当于看有没有和这个皇后构成斜率为1的直线,也就是对于两个皇后q1(x1,y1), q2(x2,y2),看(x1-x2)的绝对值和(y1-y2)的绝对值是否相等,用以下代码实现:
二维数组:
if(Math.abs(Math.abs(row-i) - Math.abs(col - j)) < 0.001 && board[i][j] == 1){
return false;
}
一维数组:
if (Math.abs(Math.abs(row - i) - Math.abs(board[row] - board[i])) < 0.001){
return false;
}
二维数组的最终代码:
public class SdgStu {
public static void main(String[] args) {
int board[][] = new int[8][8];//定义一个8*8的棋盘
BHH bhh = new BHH();
bhh.game(board, 0);
}
}
class BHH {
private int n = 0;//记录结果个数
public void game(int board[][], int row) {//row记录行数
//遍历一行中的每一列
for (int i = 0; i < 8; i++) {
if (checkBoard(board, row, i))//检测该位置是否正确,row,i为该位置的行数和列数
{
board[row][i] = 1;//将该位置设置为皇后
if (row == 7) {
print(board,n);//直到在第八行能找到满足要求的位置才打印
} else {
game(board, row + 1);
}
}
board[row][i] = 0;//将皇后设置为0,防止干扰打印下一个棋盘的结果
}
}
//检查在该位置放置皇后是否正确
public boolean checkBoard(int board[][], int row, int col) {
//判断是否有同列的皇后
for (int i = 0; i < row; i++) {
if (board[i][col] == 1) {
return false;
}
}
//之前有row个皇后,遍历前row行
for (int i = 0; i < row; i++) {
for (int j = 0; j < 8; j++) {
//判断该位置的左上和右上是否有皇后
if (Math.abs(Math.abs(row - i) - Math.abs(col - j)) < 0.001 && board[i][j] == 1) {
return false;
}
}
}
return true;
}
//打印结果
public void print(int board[][],int count) {
this.n = ++count;
System.out.println("第" + count + "个结果:");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(board[i][j] + " ");
}
System.out.print("\n");//换行
}
System.out.println();//空一行
}
}
一维数组的最终代码:
public class test {
public static void main(String[] args) {
int board[] = new int[8];
BHH bhh = new BHH();
bhh.game(board, 0);
}
}
class BHH {
private int n = 0;//记录结果个数
public void game(int board[], int row) {//row记录行数
//遍历一行中的每一列
for (int i = 0; i < 8; i++) {
board[row] = i;//将该位置设置为皇后
if (checkBoard(board, row, board[row])) {//检测该位置是否正确,row,board[row]为该位置的行数和列数
if (row == 7) {
print(board,n);
} else
game(board, row + 1);
}
//board[row] = 0;一维数组是不需要这一步的,当循环下一次时,board[row] = i会覆盖上一次的结果
}
}
public boolean checkBoard(int board[], int row, int col) {
for (int i = 0; i < row; i++) {
//判断是否同列
if (board[i] == col) {
return false;
}
//判断是否在左上和右上
if (Math.abs(Math.abs(row - i) - Math.abs(board[row] - board[i])) < 0.001){
return false;
}
}
return true;
}
//打印
public void print(int board[],int count){
this.n = ++count;
System.out.println("第" + count + "个结果:");
for(int i = 0; i < board.length; i++){
System.out.print(board[i] + " ");
}
System.out.println();//换行
}
}
运行结果: