八皇後問題(回溯算法)
問題描述
八皇後問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾于1848年提出:在8×8格的國際象棋上擺放八個皇後,使其不能互相攻擊,即任意兩個皇後都不能處于同一行、同一列或同一斜線上,問有多少種擺法。 高斯認為有76種方案。1854年在柏林的象棋雜志上不同的作者發表了40種不同的解,後來有人用圖論的方法解出92種結果。計算機發明後,有多種計算機語言可以解決此問題。
思路分析
- 第一個皇後先放第一行第一列
- 第二個皇後放在第二行第一列、然後判斷是否OK,如果不OK,繼續放在第二列、第三列、依次把所有列都放完,找到一個合适
- 繼續第三個皇後,還是第一列、第二列…….直到第8個皇後也能放在一個不沖突的位置,算是找到了一個正确解
- 當得到一個正确解時,在棧回退到上一個棧時,就會開始回溯,即将第一個皇後,放到第一列的所有正确解,全部得到.
- 然後回頭繼續第一個皇後放第二列,後面繼續循環執行1,,2,3的步驟
說明: 理論上應該建立一個二維數組來表示棋盤,但是實際上可以通過算法,用一個一維數組即可解決問題。arr[8]={0,4,7,5,2,6,1,3}//對應arr下标 表示第幾行,即第幾個皇後,arr[i]=val,val表示第i+1個皇後,放在第i+1行的第val+1列。
代碼實作
package com.recursion;
public class Queue8 {
// 定義一個max表示共有多少個皇後
int max = 8;
// 定義一個array,儲存皇後放置位置的結果,比如arr= {0,4,7,5,2,6,1,3}
int[] array = new int[max];
static int count = 0;
static int judgeCount = 0;
public static void main(String[] args) {
// 測試八皇後
Queue8 queue8 = new Queue8();
queue8.check(0);
System.out.println("一共有" + count + "解法");
System.out.println("一共判斷沖突" + judgeCount + "次");
}
// 方法:放置第n個皇後
// 特别注意:check是每一次遞歸時,進入到check中都有for (int i = 0; i < max; i++),是以産生回溯
private void check(int n) {
if (n == max) {// n=8,八個皇後就已經放好了
print();
return;
}
// 依次放入皇後,并判斷是否沖突
for (int i = 0; i < max; i++) {
// 先把目前皇後你,放到該行的第一列
array[n] = i;
// 判斷當放置第n個皇後到i列時,是否沖突
if (judge(n)) {// 不沖突
// 接着放n+1個皇後,即開始遞歸
check(n + 1);
}
// 如果沖突,就繼續執行array[n]=i;即将第n個皇後,放置在本行的後移的一個位置
}
}
// 檢視當我們放置第n個皇後,就去檢測該皇後是否和前面已經擺放的皇後沖突
/**
* @param n 表示第n個皇後
* @return
*/
private boolean judge(int n) {
judgeCount++;
for (int i = 0; i < n; i++) {
// 說明:
// 1.array[i] == array[n] 判斷是否在同一列
// 2.Math.abs(n - i) == Math.abs(array[n] - array[i]) 判斷是否在同一斜線
// 3.判斷是否在通一行,沒有必要,n每次都在遞增。
if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
// 先寫一個方法可以将皇後擺放的位置輸出
private void print() {
count++;
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}