算法——八皇后问题(eight queen puzzle)之回溯法求解

八皇后谜题是经典的一个问题,其解法一共有92种!

其定义:

  1. 首先定义一个8*8的棋盘
  2. 我们有八个皇后在手里,目的是把八个都放在棋盘中
  3. 位于皇后的水平和垂直方向的棋格不能有其他皇后
  4. 位于皇后的斜对角线上的棋格不能有其他皇后
  5. 解出能将八个皇后都放在棋盘中的摆法

这个问题通常使用两种方法来求解:

  1. 穷举法
  2. 回溯法(递归)

本文章通过回溯法来求解,回溯法对比穷举法高效许多,让我们学习如何实现吧!

实现思想:

  1. 我们先在棋盘的第0行第1个棋格放下第一个皇后
  2. 下一行寻找一个不冲突的棋格放下下一个皇后
  3. 循环第2步
  4. 如果到某一行全部8个格子都无法放下皇后,回溯到前一行,继续寻找下一个不冲突的棋格
  5. 把8个皇后都放在棋盘之后,输出或存储摆法,结束

实现(Java)算法:

定义棋盘

我们通过一个二维整型数组表示一个棋盘

数组内为1是放下了的皇后,0则是空白的棋格

我们下下面定义一个方法:通过检查棋格是否为1来知道是不是有皇后

1     // 定义一个棋盘
2     static int chessboard[][] = new int[8][8];

检查冲突

这个方法用来检查冲突:在水平垂直方向、斜角上的棋格有无其他皇后,传入的(x,y)是需要检查的棋格,如检查棋格(1,0)即棋盘的第2行第1个,是否能放下皇后。

 1     // 检查是否符合规则
 2     private static boolean checked(int x,int y){
 3         for(int i = 0;i<y;i++){
 4             // 检查水平垂直方向
 5             if(chessboard[x][i]==1)return false;
 6             // 检测左斜角
 7             if((x-y+i>=0)&&chessboard[x-y+i][i]==1)return false;
 8             // 检查右斜角
 9             if((x+y-i<=7)&&chessboard[x+y-i][i]==1)return false;
10         }
11         return true;
12     }

放下皇后

我们在每一行都执行以下步骤,通过从第1个棋格到第8个遍历寻找可以放下皇后的棋格

如果放下了皇后,我们就可以继续放下下一个了,将行数+1,我们递归调用这个方法

 1     public static boolean solve(int y){
 2         // 将一行的8种情况都扫描一次
 3         for(int i = 0;i<8;i++){
 4             // 每次检测前都将当前行清空,避免脏数据
 5             for(int k = 0;k<8;k++)chessboard[k][y]=0;
 6             if(checked(i, y)){
 7                 chessboard[i][y] = 1;
 8                 // 当前一行已经获得解法,进入下一行
 9                 solve(y+1);
10             }
11         }
12         return false;
13     }

算法边界

当我们放下了所有8个皇后后,需要一个终止条件,我们在行数y=8时,结束算法

同时你可以输出一个棋盘摆法了!恭喜你已经把这个经典问题解决了!

1         // 当y=8时,已经找到一种解决方法
2         if(y == 8){
3             return true;
4         }

以下是完整的算法

 1 public class EightQueen{
 2     // 定义一个棋盘
 3     static int chessboard[][] = new int[8][8];
 4     // 计数器
 5     static int count = 0;
 6 
 7     // 解题方法
 8     public static boolean solve(int y){
 9         // 当y=8时,已经找到一种解决方法,计数器加一并输入摆法
10         if(y == 8){
11             System.out.println("solved!");
12             show();
13             count++;
14             return true;
15         }
16         // 将一行的8种情况都扫描一次
17         for(int i = 0;i<8;i++){
18             // 每次检测前都将当前行清空,避免脏数据
19             for(int k = 0;k<8;k++)chessboard[k][y]=0;
20             if(checked(i, y)){
21                 chessboard[i][y] = 1;
22                 // 当前一行已经获得解法,进入下一行
23                 solve(y+1);
24             }
25         }
26         return false;
27     }
28     // 检查是否符合规则
29     private static boolean checked(int x,int y){
30         for(int i = 0;i<y;i++){
31             // 检查垂直方向
32             if(chessboard[x][i]==1)return false;
33             // 检测左斜角
34             if((x-y+i>=0)&&chessboard[x-y+i][i]==1)return false;
35             // 检查右斜角
36             if((x+y-i<=7)&&chessboard[x+y-i][i]==1)return false;
37         }
38         return true;
39     }
40     // 输出棋盘摆法
41     public static void show(){
42         for(int i = 0;i<8;i++){
43             for(int j = 0;j<8;j++){
44                 System.out.print(chessboard[j][i]+"  ");
45             }
46             System.out.println("");
47         }
48     }
49 }

在执行这个算法后:

have 92 ways to sovle it!

我们获得了92种棋盘摆法!

 

原文地址:https://www.cnblogs.com/joe2047/p/10486573.html