八皇后的问题大家应该都不陌生,问题简单描述如下:
在8*8的国际象棋棋盘上,摆放8个皇后,使它们两两间不能互相攻击(不能在同一行,同一列,同一对角线)。
上学时,用的回溯的算法解决,今天重新思考这个问题,发现用递归也很容易实现。
分析:要在n*n的棋盘上摆放n个皇后,则至少每一行,每一列有且只有一个皇后。问题解决的必要条件是在前n-1列的棋盘上要摆放n-1个皇后。再检查每n列的每一格:在该位置上摆放皇后在前面n-1列得到的解,测试是否皇后间产生攻击,即可。依此类推,最后问题转换成在棋盘的第一列上摆放1个皇后,显然,解法有n种。
以XiYi、XjYj表示两个皇后的位置坐标,两个皇后能攻击的条件:- 在同一行:纵坐标相同,即Yi=Yj
- 在同一列:横坐标相同,即Xi=Xj
- 在同一正对角线:所有的正对角线为一组线性方程为x+y=N(0<=N<8)的直线,所以Xi+Yi=Xj+Yj=N
- 在同一副对象线:所有的副对角线为一组线性方程为x-y=N(0<=N<8)的直线,所以Xi-Yi=Xj-Yj=N
直接上代码:
package queen8;
import java.util.ArrayList;
import java.util.List;
public class EightQueenTest {
public static void main(String[] args) {
print(listQueue(8, 8));
}
/**
* 在n*n的棋盘,前column列摆放皇后column个皇后的所有解法
* @param n 棋盘边长
* @param column 列数/皇后数
*/
public static List<int[][]> listQueue(int n, int column) {
List<int[][]> result = new ArrayList<>();
// 只有一列的情况,显然共row种解法
if (1 == column) {
for (int i = 0; i < n; i++) {
int[][] arr = new int[n][n];
arr[i][0] = 1;
result.add(arr);
}
} else {
List<int[][]> subResult = listQueue(n, column - 1);
// 遍历第column列,第1行到第n行,n格,与column-1列的所有解法比较,看是否是一个解
for (int i = 0; i < n; i++) {
for (int j = 0; j < subResult.size(); j++) {
int[][] arr = subResult.get(j);
int[][] testArr = test(arr, n, column - 1, i, column - 1);
if (null != testArr) {
result.add(testArr);
}
}
}
}
return result;
}
/**
* 判断坐标为[row, column]的皇后与arr中皇后是否产生攻击,是则返回null,反之,则返回解
* @param arr
* @param n
* @param m
* @return
*/
public static int[][] test(int[][] arr, int n, int m, int row, int column) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (1 == arr[i][j] && attacked(n, i, j, row, column)) {
return null;
}
}
}
int[][] arrcp = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
arrcp[i][j] = arr[i][j];
}
arrcp[row][column] = 1;
}
return arrcp;
}
/**
* 判断坐标为[row1, column1]的[row2, column2]的皇后能否相互攻击到对方
* @param row1
* @param column1
* @param row2
* @param column2
* @return
*/
public static boolean attacked(int n, int row1, int column1, int row2, int column2) {
if (row1 == row2) {// 同一行
return true;
} else if (column1 == column2) {// 同一列
return true;
} else {// 同一对角线(正对角线满足y=x,副对角线满足x+y=n)
return (row1 + column1 == row2 + column2)
||
(row1 - column1 == row2 - column2);
}
}
public static void print(List<int[][]> result) {
System.out.println("总共有" + result.size() + "种解法。");
for (int i = 0; i < result.size(); i++) {
System.out.println("第" + (i + 1) + "种解法:");
int[][] arr = result.get(i);
for (int j = 0; j < arr.length; j++) {
int[] subArr = arr[j];
for (int k = 0; k < subArr.length; k++) {
System.out.print(subArr[k]==1? "W" : "-");
}
System.out.println();
}
}
}
}
最终算得8皇后是92种解法,当把棋盘扩大到13时,程序运行内存溢出了。。。