八皇后问题是什么?
八皇后问题,一个古老而著名的问题,是回溯算法的典型案例。该问题由国际西洋棋棋手马克斯·贝瑟尔于 1848 年提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
如何解决?
1.我们可以一行行的来放棋子,一行一个棋子,这样就解决了任意两个皇后不在同一行的要求;
2.按照顺序来,从第一行从第一列开始位置开始放棋子;在满足与第一个棋子不在同一行,同一列,同一斜线上的第二行得某个位置再放入棋子;在满足与之前放过的棋子不在同一行,列,斜线上再放入第三个棋子;......以此类推;
3.如果顺利进行到在第八行放第八个棋子依然满足与之前放过的棋子不在同一行,列,斜线上,则这种放法的八个棋子都满足条件,输出;
4.如果中途某一行无论放在那一个位置都不能满足与之前放过的棋子不在同一行,列,斜线上,则这种放法作废,不输出;
如何用Java代码实现以上解决方案?
1.首先由于我们是按顺序每一行的放一个棋子,不在同一行的标准就可以解决,那么也就不需要二维数组来模拟棋盘。因为是按顺序的,所以用一个有八个元素的一位数组来解决,下标的值代表行数,从0到7模拟1到8行;其中的元素代表列,存入0到7模拟1到8列(如arr【i】=j就代表在第i行第j列放入了棋子)
1.其次我们在一步步放置棋子的过程中,都要满足一个需求:与之前放过的棋子不在同一行,列,斜线。也就是这是我们放棋子的准则,这样每一步都遵循同一种方法且每放一个棋子都依赖之前的棋子的放法,让我们想到了递归;
2.那么采用递归的方法,就要有一个终止条件或者说叫最终的目的,那就是在第八行放棋子仍然满足与之前放过的棋子不在同一行,列,斜线上,到这一步一种放法才算成功;
Java代码(含注释)
1.由于是按顺序每一行放一个棋子,所以只需编写一个判断放入棋子和之前棋子是否满足不在同一行,列,斜线上的方法,如果放入该位置的棋子满足标准,返回true,反之返回false。
public boolean verify (int[] array,int n) {//参数为模拟棋盘的一维数组和要放棋子的行数n+1
for (int i = 0; i < n; i++) {
if((Math.abs(i-n)==Math.abs(array[i]-array[n]))||array[i]==array[n]) {
//行数相减与列数相减相等代表两棋子在同一斜线上
//不同下标存放的元素相等代表在同一列上
//若满足其中情况的一种就不符合要求,返回false
return false;
}
}
//否则返回true
return true;
}
2.递归方法:放置第i行的棋子
public void eightQueens(int[] board ,int i) {//参数为模拟棋盘的一位数组和要放入棋子的行数i+1
if(verify(board,7)) {//放入第八个棋子时,若满足条件,则输出
count++;//类中的属性,全局变量用于计数
System.out.println("第"+count+"种情况");
print(creatArray(board));//creatArray()方法用于将模拟的以为数组转化为直观的二维数组
//print()用于输出二维数组
}
else {
for (int j = 0; j < 8; j++) {//j代表j列
board[i]=j;将i行j列放入棋子
if (verify(board,i)) {
//判断条件为1中函数成立,即满足放入棋子与之前棋子不在同一行同一列同一斜线
//若满足条件进行下一行棋子的放置
eightQueens(board,i+1);
}
//若j从0到7没有满足条件的就不再调用下一个棋子的eightQueens方法,也就无法进行到
//判断verify(board,7)的一步,就无法输出,回溯法的体现
}
}
}
3.creatArray()方法:将模拟棋盘的一维数组转化为直观的二维数组二位数组
public char[][] creatArray(int[] array) {//参数为模拟棋盘的一维数组
char[][] ret = new char[array.length][array.length];//创建一个8*8的二位数组
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length ; j++) {
if(array[i]==j) {
ret[i][j]='Q';//将放入棋子的以'Q'表示
}else {
ret[i][j]='*';//将没放入棋子的以'*'表示
}
}
}
return ret;//返回这个直观的二维数组
}
4.print()方法:遍历输出棋盘(二维数组)
public void print(char[][] array) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j]+" ");
}
System.out.println();
}
}
5.总代码
public class EightQueen {
public static void main(String[] args) {
Recursion05 r = new Recursion05();
int[] board=new int[8];
r.eightQueens(board,0);
System.out.println("一共"+r.count+"种");
}
}
class Recursion05 {
int count=0;
public void eightQueens(int[] board ,int i) {
if(verify(board,7)) {
count++;
System.out.println("第"+count+"种情况");
print(creatArray(board));
}
else {
for (int j = 0; j < 8; j++) {
board[i]=j;
if (verify(board,i)) {
eightQueens(board,i+1);
}
}
}
}
public boolean verify (int[] array,int n) {
for (int i = 0; i < n; i++) {
if((Math.abs(i-n)==Math.abs(array[i]-array[n]))||array[i]==array[n]) {
return false;
}
}
return true;
}
public char[][] creatArray(int[] array) {
char[][] ret = new char[array.length][array.length];
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length ; j++) {
if(array[i]==j) {
ret[i][j]='Q';
}else {
ret[i][j]='*';
}
}
}
return ret;
}
public void print(char[][] array) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j]+" ");
}
System.out.println();
}
}
}
6.运行结果
(ps.如果有不足,希望各位大佬纠正,定会虚心改进)