1、问题描述
在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子,如图a所示。n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上,如图b所示
算法分析
- 用x[i]表示皇后i放在棋盘的第i行的第x[i]列, x[j]表示皇后j放在棋盘的第j行的第x[j]列。
- 由于不允许将2个皇后放在同一行所以:i≠j 由于不允许将2个皇后放在同一列所以:x[i]≠ x[j]
- 由于2个皇后不能放在同一斜线上,即不处于同一正、反对角线,所以:
- |i-j|≠| x[i]-x[j]| 因此,n皇后彼此不受攻击的条件为:i≠j且x[i]≠ x[j]且|i-j|≠|x[i]-x[j]|
这种问题可以采用回溯法,
皇后1在第一行放置后,
皇后2在第二行放置的位置依赖于皇后1放置的位置,
皇后3在第三行放置的位置依赖于皇后1和2的位置
…
Java实现代码
import static java.lang.Math.abs;
public class N_queen {
int[] x;//x存储皇后的位置,下标加1代表行数,下标对应的值代表列数
int n ;//n代表层数或者皇后位数
public N_queen(int n){
this.n=n;
x=new int[n+1];
}
public void backtrack(int t) {
//t代表所在的层数,从t=1开始
if (t > n) { //t>n表示已经达到叶节点
// 打印结果
for (int a = 1; a <= n; a++) {
System.out.print("(第" + a+"行"+ "第"+x[a]+"个) ");
}
System.out.println();
} else
for (int i = 1; i <= n; i++) {
//i代表每个节点下都有4个节点
//t记录第t行 x[t]记录第t行放置皇后的位置
x[t] = i;
//如果x[t]可以放置皇后,则继续进行深度搜索
if (place(t)){
backtrack(t + 1);
}
}
}
//判断(k行i列)是否可以放置皇后
private boolean place(int k) {
for (int j = 1; j < k; j++)
//(x[j] == x[k]代表列对齐
//abs(k - j) == abs(x[j] - x[k])代表斜线对齐
if ((x[j] == x[k]) || (abs(k - j) == abs(x[j] - x[k])))
return false;
return true;
}
public static void main(String[] args) {
N_queen n_queen=new N_queen(4);
n_queen.backtrack(1);
}
}
运行结果
棋盘表示
JavaScript实现
// 立即执行函数
(function () {
let n = 4;// 皇后的位数
let x = new Array(n + 1);//x记录皇后的位置
let str;
function backTrack(t) {//t为所在n叉树的层数
// 当前节点是叶节点 则打印
if (t > n) {
str = "";
for (let a = 1; a <= n; a++) {
str += "(第"+a+"行,"+"第"+ x[a]+"列) ";
}
console.log(str);
} else {
for (let i = 1; i <= n; i++) {
x[t] = i;//皇后t放置在棋盘的第t行第i列
//判断皇后t放置的位置是否正确 正确则继续深度搜索
if (place(t)) {
backTrack(t + 1);
}
}
}
}
//判断皇后k放置的位置是否正确
function place(k) {
for (let j = 1; j < k; j++) {
//x[j] == x[k] 列对齐
//Math.abs(j - k) == Math.abs(x[j] - x[k]) 斜对齐
if (x[j] == x[k] || Math.abs(j - k) == Math.abs(x[j] - x[k]))
return false;
}
return true;
}
backTrack(1);
})();