该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果,如下是一种:
本算法的思路是按行来规定皇后位置,第一行放置一个皇后,第二行放置一个皇后, 第N行也放置一个皇后… 这样, 可以保证每行都有一个皇后,那么各行的皇后应该放置在那一列呢, 算法通过循环来完成,在循环的过程中, 一旦找到一个合适的列,则该行的皇后位置确定,则继续进行下一行的皇后的位置的确定。由于每一行确定皇后位置的方式相似,所以可以使用递归法。一旦最后 一行的皇后位置确定,则可以得到一组解。找到一组解之后, 之前确定皇后应该放置在哪一列的循环其实才进行了一轮循环的, 算法通过该循环遍历所有的列,以此确定每一行所有可能的列的位置。在从一轮循环进入下一轮循环之前,算法需要清除在上一轮被标记为不可放置皇后的标记,也就是回溯。因为进入下一轮循环之后,同一行的皇后的列的位置会发生了变化,之前被标记为不可放置皇后的列和正反对角线位置都已经失效
代码实现
package com.gt.java.algorithm;
/**
* 八皇后问题
*/
public class EightQueens {
private int QUEEN_COUNT = 0;//皇后的默认数量
private int[][] chessboard;//分配8*8的数组,充当棋盘,存放皇后
private int recordCount = 0;//记录皇后的放置方法的总数
private int[] queenPlace;//对于索引n,queenPlace[n]表示第n行的皇后位置是queenPlace[n]列
public EightQueens(int n) {
this.QUEEN_COUNT = n;
this.recordCount = 0;
this.chessboard = new int[QUEEN_COUNT][QUEEN_COUNT];
this.queenPlace = new int[QUEEN_COUNT];
}
public void putQueen(){
putQueen(0);
}
/**
* 放置皇后
* @param row 行
*/
private void putQueen(int row) {
//column表示列标,该层循环的作用是用于询问第row行,第column列是否可以放置皇后
for (int column = 0;column<this.QUEEN_COUNT;column++){
if(chessboard[row][column]==0){//表示第row行,第column列可以放置皇后
//该层for循环的作用是使其正下方和斜下方的位置不可放置元素(将元素置1)
for (int nextRow = row+1;nextRow<QUEEN_COUNT;nextRow++){
//将正下方的元素置1
chessboard[nextRow][column]++;
//将其正斜线位置的元素置1
int obliqueColumn = column-nextRow+row;
if (obliqueColumn>=0){
chessboard[nextRow][obliqueColumn]++;
}
//将其反斜线位置的元素置1
obliqueColumn = column+nextRow-row;
if (obliqueColumn<this.QUEEN_COUNT){
chessboard[nextRow][obliqueColumn]++;
}
}
//记录下第row行第column列放置了皇后
queenPlace[row] = column;
//如果各行都放置了皇后,也就说明皇后已放满,打印出皇后的布局
if (row == this.QUEEN_COUNT-1){
printQueen(++this.recordCount);
}else {// 否则递归继续排列下一行皇后
putQueen(row+1);
}
//回溯,使得回溯,使得在第row行的皇后不放在第column列,那放置在那一列?
//答案是通过该算法的最外层循环,利用最外层for循环将皇后放在这一行的其他列
for (int rows = row+1;rows<this.QUEEN_COUNT;rows++){
//既然第row行、第column列不放置皇后了,
// 则需要恢复正下方的不可用标记,将不同行的同一列的非零标记还原,也即恢复该位置的正下面的标记
//恢复正下方的标记
chessboard[rows][column]--;
//恢复其正斜线方向位置的元素
int obliqueColumn = column-rows+row;
if (obliqueColumn>=0){
chessboard[rows][obliqueColumn]--;
}
//恢复其反斜线方向位置的元素
obliqueColumn = column+rows-row;
if (obliqueColumn<this.QUEEN_COUNT){
chessboard[rows][obliqueColumn]--;
}
}
}
}
}
/**
* 打印皇后布局
* @param size
*/
private void printQueen(int size) {
System.out.println(this.QUEEN_COUNT+"皇后的第"+size+"个解是:");
System.out.println();
for (int row = 0;row<this.QUEEN_COUNT;row++){
for (int column = 0;column<this.QUEEN_COUNT;column++){
System.out.print(queenPlace[row]==column?"1":"0");
}
System.out.println();
}
System.out.println();
}
public static void main(String[] args) {
EightQueens eightQueens = new EightQueens(8);
eightQueens.putQueen();
}
}