八皇后
题目:
- 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
问题分析:
简单的说问题就是,在8×8格的棋盘中要求同行同列同斜线不能有重复的皇后棋子,问多少种摆法。
用递归的方法实现,递归就像找文件一样,一个文件夹下面有若干文件,不确定文件在哪,先找第一个文件夹,第一个文件夹下面还有文件夹,继续往下找,若找不到,则返回上一个文件夹,找同级文件夹内,直到找到,每个文件夹内都是一个for循环。
对于八皇后问题也可以这样考虑,首先初始化棋盘,将棋盘copy一份,在copy的棋盘中操作,先将皇后放在第一行第一个,第二个皇后在第二行中找不冲突的位置放,依次向下放皇后,若位置冲突,则换该行其他格子放。若该行格子都不能放皇后,说明前面皇后放的有问题,删掉上一个皇后,重新考虑上一个皇后位置,依此逻辑,直到找到所有的摆法。
八皇后递归实现核心代码:
public static void eightQueen(int row,int[][] board){
if(row==8){
count++;
System.out.println("第"+count+"种可能");
print(board); //打印棋盘
System.out.println();
}else{
int[][] newBoard=copyOf(board); //拷贝一个蓝本
for(int col=0;col<8;col++){
if(isNoDanger(row,col,newBoard)){ //该位置不冲突时
for(int c=0;c<8;c++){ //清行代码
newBoard[row][c]=0;
}
newBoard[row][col]=1; //放皇后
eightQueen(row+1,newBoard); //递归
}
}
}
}
核心代码解析:
第一次进来,row=0,意思是要在第一行摆皇后,只要传进来的row参数不是8,表明还没出结果,那么就进入到for循环里面,copy一个蓝本,进行放置皇后操作,col从0开始,即第一列。此时第一行第一列肯定合乎要求,能放下皇后,因为还没有任何其他皇后来干扰。
若皇后放置也不冲突(isNoDanger方法),在if里面又会调用一下自己(即递归),row加了1,表示摆第二行的皇后了。第二行的皇后在走for循环的时候,分两种情况,
-
第一种情况:for循环没走到头时就有通过isNoDanger方法的,那么就往下走再调用一下自己(即再往下递归),row再加1(即摆第三行的皇后了,以此类推)。
-
第二种情况:for循环走到头了都没有通过isNoDanger方法的,说明第二行根本一个皇后都摆不了,也触发不了递归,后面的就不用提了,此时控制第一行皇后位置的for循环col加1,即第一行的皇后往后移一格,然后再往下走,重复上述逻辑。
注意:一定要添加清行的代码,它只有在皇后摆不下去的时候会执行清0的动作(避免脏数据干扰)
这段核心代码的原理弄懂,八皇后问题就很好解决了,八皇后问题一共有92种情况,下面是用Java实现的完整代码:
public class EightQueen {
static int count=0; //有多少只可能
public static void main(String[] args) {
int[][] board=new int[8][8];
eightQueen(0,board);
}
//递归函数
public static void eightQueen(int row,int[][] board){
if(row==8){
count++;
System.out.println("第"+count+"种可能");
print(board); //打印棋盘
System.out.println();
}else{
int[][] newBoard=copyOf(board); //拷贝一个蓝本
for(int col=0;col<8;col++){
if(isNoDanger(row,col,newBoard)){ //该位置不冲突时
for(int c=0;c<8;c++){
newBoard[row][c]=0;
}
newBoard[row][col]=1; //放皇后
eightQueen(row+1,newBoard); //递归
}
}
}
}
//判断该位置是否与其他皇后冲突
private static boolean isNoDanger(int row, int col, int[][] newBoard) {
//向上
for(int r=row-1;r>=0;r--){
if(newBoard[r][col]==1){
return false;
}
}
//向左上
for(int r=row-1,c=col-1;r>=0&&c>=0;r--,c--){
if(newBoard[r][c]==1){
return false;
}
}
//向右上
for(int r=row-1,c=col+1;r>=0&&c<8;r--,c++){
if(newBoard[r][c]==1){
return false;
}
}
return true;
}
//复制棋盘
private static int[][] copyOf(int[][] board) {
int[][] newBoard=new int[board.length][board.length];
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
newBoard[i][j]=board[i][j];
}
}
return newBoard;
}
//打印棋盘
private static void print(int[][] board) {
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
System.out.print(board[i][j]+" ");
}
System.out.println();
}
}
}
重点是核心代码部分,即递归的思想,递归思想了解清楚,递归类的题就好做很多。