1.问题描述
在国际象棋中,皇后可以横,竖,斜三种走法. (比中国象棋的車还厉害些)
现有n*n的棋盘 要放n个皇后,且使他们相互无法攻击,问有多少种放法.(即棋盘上任意横,竖斜线都只有一个皇后)
2.大概思路:
1.用n*n一维数组表示结果:0代表未放子,1表示放子. 根据下标自己逻辑解析为n*n二维
2.判断一行的每个格子是否可以落子,如果可以则落子后递归到下一行继续判断.直到第n+1行,即算出结果可以打印出来.
3.注意的点:如果落错子记得回退,
落子后如果下一行所有格子都不能放则说明上一处落子错误需要回退.(也算是回溯算法)
public void queen(int[] arr, int n, int row) {
if (row == n) {
printQueen(arr, n);//求解成功,打印结果
} else {
for (int i = 0; i < n; i++) { // 循环当行 每个格子是否可以落子
if (canPlace(arr, n, row * n + i)) { // 判断是否可落子
arr[row * n + i] = 1; // 落子
queen(arr, n, row + 1); // 递归到下一行
arr[row * n + i] = 0; // 回退,说明此处落子后面某行可能无法落子
}
}
}
}
判断是否可以落子
/**
*判断是否可以落子
*/
public canPlace(int[] arr, int n, int i){
int row = i/n;
int col = i%n;
for(int j=0;j<row;j++ ){// 从第0行看是否有子
if(a[j*n+col]>0) return false;
if(col-(row-j)>=0 && a[j*n+col-(row-j)]>0) return false;
if(col+(row-j)<n && a[j*n+col+(row-j)]>0) return false;
}
for(int j=0;j<col;j++){
if(a[j+row*n]) return false; // 第row行不能落子
}
return ture;
}
据说有个更简单的判断: 假设(row,col) 处有落子,判断(i,j)是否可以落子可用公式:
col =j (是否在一列) 和 |row-i|=|col-j| (是否在斜线上)
代码表示:
if (col == j || Math.abs(row - i) == Math.abs(col - j)) return false;
3.java实现
package arithmetic;
/**
* @author jiangfeng 2019/8/4 17:59
* 八皇后问题
*/
public class EightQueen {
static int count =0;
public static void main(String[] args) {
queen(8);
}
/**
* n皇后问题的递归解法.
* todo 回溯算法求解
* @param n
*/
public static void queen(int n) {
int[] arr = new int[n * n];
queen_(0, arr, n);
System.out.println("有解数:" + count);
}
/**
* 八皇后问题递归求解器
* @param row 当前递归的行数
* @param arr 八皇后棋盘数组 n*n一维,根据下标自己逻辑解析为n*n二维
* @param n 棋盘的大小
*/
private static void queen_(int row, int[] arr, int n) {
if (row == n) {
printResult(arr);
System.out.println();
count++;
} else {
for (int i = 0; i < n; i++) { //试探row行的 每一列是否可放
if (canPlace(arr, i + row*n, n)) {
arr[i + row*n] = 1;
queen_(row + 1, arr, n);//递归到下一行去
arr[i + row*n] = 0; // 说明此处不能放子
}
}
}
}
/**
* 检测是否横竖斜均无棋子
* @param arr
* @param i 第i个棋子是否可放
* @return
*/
private static boolean canPlace(int[] arr, int i, int n) {
// 第i个棋子的 左上,正上,右上不能有棋子
int row = i / n;
int col = i % n;
for (int j = 0; j < row; j++) { // 第row行不用考虑
if (arr[j * n + col] > 0) { // 正上不能有子
return false;
}
if (col - row + j >= 0 && arr[j * n + col - row + j] > 0) { // 左斜上方不能有子
return false;
}
if (col + row - j < n && arr[j * n + col + row - j] > 0) { // 右斜上方不能有子
return false;
}
}
for (int j = 0; j < col; j++) {
if (arr[row * n + j] > 0) { // 第row行不能落子
return false;
}
}
return true;
}
/**
* 用一维数组表示结果:0代表未放子,1表示放子
* 打印结果
* @param arr
*/
private static void printResult(int[] arr){
int n = ((Double) Math.sqrt(arr.length)).intValue();// 开平方
for (int i = 0; i < arr.length; i++) {
if ((i + 1) % n == 0) {
System.out.println(arr[i]);
} else {
System.out.print(arr[i] + " ");
}
}
}
}
结果: