1. 问题描述:
请设计一种算法,解决著名的n皇后问题,这里的n皇后问题指在一个n * n的棋盘上放置n个棋子,
使得每行每列和每条对角线上都只有一个棋子,求其摆放的方法数。
给定一个int n,请返回方法数,保证n小于等于15
2. 经典的n皇后,我们最常用的是使用深度优先搜索算法dfs来解决,因为在棋盘上的位置上放置一个棋子就是一种试探,并且假如这个棋盘放置好棋子但是之后的棋子无论如何都不能放置在棋盘上,说明原来这个位置上放置的棋子是不合理的,需要撤销掉,这相当于dfs中的回溯,然后尝试再棋盘的下一列上放置棋子,然后继续尝试下去,这实际上就是dfs的思想,尝试去走每一条路,假如这条路走不通,然后退回到原来的地方,然后走下一条可能的路径,所以经过搜索之后,dfs会尝试走遍所有的路找到所有可能的路径然后就结束了
这里需要使用一个辅助数组来记录棋盘上的哪些位置上已经放置有棋子了,我们可以使用一维数组来进行记录,其中一维数组的下标来表示棋子的行数,下标对应的值表示的是该位置上棋子对应的列,假如数组相应位置的值大于零那么就表示了这个确定的位置上放置有棋子
3. 因为题目中只要求出摆放的方法数,而不需要求解出其中的过程那么我们就不需要进行回溯,此外这个题目不需要进行回溯的另外一个原因是回退到平行状态的时候我们不需要考虑同行的棋子对现在退回来的状态的影响,因为位于for循环中我们进入平行状态的同一行的下一列考虑这个位置上是否可以摆放,所以我们不用进行回溯只需要记录其中的摆放的方法数即可
具体的代码如下:
import java.util.Scanner;
public class Main{
static int n;
static int count = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int rec[] = new int[n];
dfs(rec, 0);
System.out.println(count);
sc.close();
}
private static void dfs(int rec[], int row){
if(row == n){
count++;
return;
}
for(int col = 0; col < n; col++){
//检查放在这里是否可以
if(check(rec, row, col)){
rec[row] = col;
dfs(rec, row + 1);
//不用回溯是因为回到这一层的时候不用考虑它的同一行 因为同一行没有影响 而且不用输出结果
//rec[row] = 0;
}
}
}
private static boolean check(int rec[], int row, int col){
//检查同列:rec[i] == col 主对角线:rec[i] - i == col - row 附对角线 :rec[i] + i == col + row
for(int i = 0; i < row; i++){
if(rec[i] - i == col - row || rec[i] + i == col + row || rec[i] == col){
return false;
}
}
return true;
}
}
其中主对角线的检查与附对角线的检查可以写出一个简单的例子来发现其中的行数与列数之间存在什么规律
以 (3, 2) 为例,可以发现它的主对角线上的:行 - 列 = x - y,附对角线上:行 + 列 = x+ y而我们使用了一维数组来记录所以可以检查一维数组行与列之间的对应来检查同列,主对角线,附对角线上是否有皇后
4. 假如我们需要记录棋盘摆放的具体的情况我们就需要在退回来的时候进行回溯把原来填进去的皇后给删除掉,然后尝试去走下一条路径,具体的代码如下:
import java.util.Scanner;
public class Main{
static int n;
static int arr[][];
static int rec[];
static int count = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
arr = new int[n][n];
rec = new int[n];
dfs(0);
//System.out.println(count);
sc.close();
}
private static void dfs(int row){
if(row == n){
count++;
System.out.println(count);
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
System.out.print(arr[i][j] + " ");
}
System.out.print("\n");
}
System.out.print("\n");
return;
}
for(int col = 0; col < n; col++){
if(check(row, col)){
arr[row][col] = 1;
rec[row] = col;
dfs(row + 1);
//回溯,清除掉原来的填进去的数字
arr[row][col] = 0;
rec[row] = 0;
}
}
}
private static boolean check(int row, int col){
for(int i = 0; i < row; i++){
if(rec[i] - i == col - row || rec[i] + i == col + row || rec[i] == col){
return false;
}
}
return true;
}
}
5. 在控制台中输入4,输出棋盘的具体情况: