N皇后问题是指在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行、不同列,也不在同一条斜线上。
给定一个整数n,返回n皇后的摆法有多少种。
举例: n=1,返回1。
n=2或3,2皇后和3皇后问题无论怎么摆都不行,返回0。
n=8,返回92。
public class Code09_NQueens {
public static int num1(int n) {
if (n < 1) {
return 0;
}
int[] record = new int[n]; // record[i] --> i 行的皇后放在了第几列
return process1(0, record, n);
}
// i:表示来到了第几行
// record:之前摆放的皇后,都在这个数组中,即之前 i-1 行的皇后的摆放位置
// n:表示整体一共多少行
// 返回值是:摆完所有的皇后,合理的摆法有多少种
public static int process1(int i, int[] record, int n) {
if (i == n) {
return 1;
}
int res = 0;
// 在 i 行,尝试所有的列:j
for (int j = 0; j < n; j++) {
// 当前i行的皇后,放在第j列,会不会和之前(0...i-1)的皇后共行/共列/共斜线
if (isValid(record, i, j)) {
// 如果是,则认为该位置是无效的
// 如果不是,则认为该位置是有效的
record[i] = j;
res += process1(i + 1, record, n);
}
}
return res;
}
public static boolean isValid(int[] record, int i, int j) {
for (int k = 0; k < i; k++) {
if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {
return false;
}
}
return true;
}
public static int num2(int n) {
if (n < 1 || n > 32) {
return 0;
}
int upperLim = n == 32 ? -1 : (1 << n) - 1;
return process2(upperLim, 0, 0, 0);
}
// colLim : 列的限制,1的位置不能放皇后,0的位置可以(采用二进制来计算)
// leftDiaLim :左斜线的限制,1的位置不能放皇后,0的位置可以(采用二进制来计算)
// rightDiaLim :右斜线的限制,1的位置不能放皇后,0的位置可以(采用二进制来计算)
public static int process2(int upperLim, int colLim, int leftDiaLim,
int rightDiaLim) {
if (colLim == upperLim) {
return 1;
}
int pos = 0;
int mostRightOne = 0;
// 所有候选的皇后位置都在 pos 上,1为可选位置,0为不可选位置
pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
int res = 0;
while (pos != 0) {
mostRightOne = pos & (~pos + 1);
pos = pos - mostRightOne;
res += process2(upperLim, colLim | mostRightOne,
(leftDiaLim | mostRightOne) << 1,
(rightDiaLim | mostRightOne) >>> 1);
}
return res;
}
public static void main(String[] args) {
int n = 14;
long start = System.currentTimeMillis();
System.out.println(num2(n));
long end = System.currentTimeMillis();
System.out.println("cost time: " + (end - start) + "ms");
start = System.currentTimeMillis();
System.out.println(num1(n));
end = System.currentTimeMillis();
System.out.println("cost time: " + (end - start) + "ms");
}
}
二 采用回溯法
/**
* 算法:回溯法
* 问题:八皇后
* @author win
*/
public class EightQueen {
public static int num = 0;//累计方案
public static final int MAXQUEEN = 8;
public static int[] cols = new int[MAXQUEEN];//定义cols数组,表示八列棋子摆放的位置
/***
*
* @param n 填第n列的皇后
*/
public void getCount(int n){
boolean [] rows = new boolean[MAXQUEEN];//记录每列每个方格是否可以放皇后。为true表示不能放
for(int m =0;m<n;m++){
rows[cols[m]] = true;
int d = n - m;//斜对角
//正斜方向
if(cols[m] - d>=0){
rows[cols[m] - d] = true;
}
//反斜方向
if((cols[m]+d)<=MAXQUEEN-1){
rows[cols[m] + d] = true;
}
}
//到此知道了哪些位置不能放皇后
for(int i = 0;i<MAXQUEEN;i++){
if(rows[i]){
//不能放
continue;
}
cols[n] = i;
if(n<MAXQUEEN-1){
getCount(n+1);
}else{
//如果n = MAXQUEEN-1;表示找到了一套完整的方案
num++;
printQueen();
}
//下面可能仍有合法位置
}
}
private void printQueen() {
System.out.println("第"+num+"种方案");
for(int i = 0;i<MAXQUEEN;i++){
for(int j=0;j<MAXQUEEN;j++){
if(i == cols[j]){
System.out.print("0 ");
}else{
System.out.print("+ ");
}
}
System.out.println();
}
}
public static void main(String[] args) {
EightQueen eightQueen = new EightQueen();
eightQueen.getCount(0);
}
}