使用回溯法解決八皇后問題(同樣適用於N皇后)。
阿新 • • 發佈:2019-01-08
在學習資料結構的時候,看到了一道八皇后的問題。寫完後,再去網上檢視發現原來這個問題有這麼多的優化解法,相比之下相形見絀。但我還是記錄下來,有興趣的小夥伴可以看看。
先大致說下我的思路
建立一個初始化值為0的8x8的陣列,0代表無皇后且不在其他皇后的攻擊範圍,-1代表皇后的位置,>0的整數代表格子在皇后的攻擊範圍。例如某個格子的數字為3說明該格子在3個皇后的攻擊範圍。
當遍歷到二維陣列的某個位置值為0,說明該位置安全,即可放置皇后,把皇后的位置設定為-1,並把該位置的豎,橫,斜方向的格子全都+1,然後就直接跳到下一行繼續查詢合適的位置放置下一個皇后。假如該行查詢完後並沒有合適的位置,這時候就會回溯上一行,先把上一行當前皇后位置設定為0,並把該位置的豎,橫,斜方向的格子全都-1,繼續往右邊格子遍歷…….以此類推。
下面是程式碼,最後求出8皇后一共有92種可能(沒排除對稱)
同樣適用於求N皇后。
import java.util.ArrayList;
import java.util.List;
/**
* 八皇后問題(N皇后問題)
*/
public class EightQueens {
private static List<int[][]> list = new ArrayList<int[][]>();
private static int[][] checkerboard;
/** N代表皇后個數 */
private static int N;
public void judge(int i, int j) {
if(checkerboard[i][j] != 0) {
checkerboard[i][j]++;
} else {
checkerboard[i][j] = 1;
}
}
/**
* 把原來該格子所在的皇后標識和攻擊範圍去除。
*/
public void toBack(int i, int m) {
for(int j = 0; j < N; j++) {
checkerboard[m][j]--;
checkerboard[j][i]--;
}
for (int x = i, y = m; x >=0 && y < N; x--, y++) {
checkerboard[y][x]--;
}
for(int x = i, y = m; x < N && y < N; x++, y++) {
checkerboard[y][x]--;
}
for(int x = i, y = m; x < N && y >= 0; x++, y--) {
checkerboard[y][x]--;
}
for(int x = i, y = m; x >= 0 && y >= 0; x--, y--) {
checkerboard[y][x]--;
}
checkerboard[m][i] = 0;
}
public boolean search(int m) {
for(int i = 0; i < N; i++) {
if(checkerboard[m][i] == 0) {
//在二位陣列中把攻擊範圍和皇后位置加上標識
for(int j = 0; j < N; j++) {
judge(m, j);
judge(j, i);
}
for(int x = i, y = m; x >=0 && y < N; x--, y++) {
judge(y, x);
}
for(int x = i, y = m; x < N && y < N; x++, y++) {
judge(y, x);
}
for(int x = i, y = m; x < N && y >= 0; x++, y--) {
judge(y, x);
}
for(int x = i, y = m; x >= 0 && y >= 0; x--, y--) {
judge(y, x);
}
checkerboard[m][i] = -1;
//當m>=N說明一種情況完成,繼續判斷下一種情況
if(++m >= N) {
//輸出該可能的皇后位置排列
/*
System.out.println("num: " + list.size() + "--------------------------");
for(int x = 0; x < N; x++) {
for(int y = 0; y < N; y++) {
if(checkerboard[x][y] < 0) {
System.out.print("1 ");
} else {
System.out.print("0 ");
}
}
System.out.println();
}
*/
list.add(checkerboard);
//把當前位置的皇后標識和攻擊範圍去除
toBack(i, --m);
//繼續下一格
continue;
}
//遞迴往下層走
if(!search(m)) {
//回溯
toBack(i, --m);
} else {
return true;
}
}
}
return false;
}
public static void main(String[] args) {
EightQueens eightQueens = new EightQueens();
for(int i = 1; i <= 14; i++) {
N = i;
checkerboard = new int[i][i];
eightQueens.search(0);
System.out.println(i + "皇后一共有" + list.size() + "種可能");
list.clear();
}
}
}
輸出結果
1皇后一共有1種可能
2皇后一共有0種可能
3皇后一共有0種可能
4皇后一共有2種可能
5皇后一共有10種可能
6皇后一共有4種可能
7皇后一共有40種可能
8皇后一共有92種可能
9皇后一共有352種可能
10皇后一共有724種可能
11皇后一共有2680種可能
12皇后一共有14200種可能
13皇后一共有73712種可能
14皇后一共有365596種可能