題目描述
八皇后問題,一個古老而著名的問題,是回溯算法的典型案例。該問題由國際西洋棋棋手馬克斯·貝瑟爾於 1848 年提出:在 8×8 格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。高斯認爲有 76 種方案。1854 年在柏林的象棋雜誌上不同的作者發表了 40 種不同的解,後來有人用圖論的方法解出 92 種結果。計算機發明後,有多種計算機語言可以編程解決此問題。
思路
1. 確定數據結構
- 初看題意,8*8的棋盤,果斷用二維數組!定義落子的地方將二維數組的座標改變即可。
- 使用二維數組固然是沒有任何問題,但是卻可以進行簡化。因爲沒兩個皇后不能再同一行或者同一列,那麼,我們以一維數組的下標來記錄行數,以一位數組的下標所在的值記錄該行皇后所在列數,那麼 index - value 就可以表現出皇后的座標
- 綜上所述,我們使用一維數組來記錄皇后位置,以index記錄第幾個皇后,在第幾行(默認第幾個皇后就在第幾行),該下標所在的值記錄皇后所在的列數,以index - value記錄皇后的座標
2. 具體實現思路
- 首先,我們定義一個可以將一維數組打印出來的方法:
print(數組)- 其次,我們來判斷當前第n個皇后與從第0個皇后開始之間的所有皇后是否處於同一行、同一列、同一斜線。
2.1 同一行不需要判斷,因爲默認一個皇后一行,不可能同行
2.2 判斷是否爲同一個斜線 因爲棋盤是正方形棋盤,那麼當兩個皇后的橫座標與縱座標相減相等時,其與他們的相交點會組成一個等腰直角三角形,此時,他們處於同一斜線上:如下圖
- 之後就是將皇后棋落子,從0開始循環到8,給第n個皇后分別賦值爲i(i爲當前循環到第幾個),之後進行判斷,是否能落子,如果能落子,那麼將n+1,繼續落子,即遞歸調用。
- 回溯 當第n個子不滿足時,該方法結束,返回調用它的方法,這時,調用它的方法會進入其方法體內的for循環,將此皇后換一個位置落,若再不行,則又會回溯。
代碼
java版本
public class Queen8 {
private static int[] array = new int[8];
private static int count = 0;
public static void main(String[] args) {
Queen8 queen8 = new Queen8();
queen8.put(0);
System.out.println(count);
}
private void put(int n){
if (n == 8){
print();
count++;
return;
}
for (int i = 0; i < 8; i++){
array[n] = i;
if (check(n)){
put(n + 1);
}
}
}
/**
* 校驗是否能落子
* @param n 第n+1個皇后
* @return 返回true 能落子 false 不能落子
*/
private boolean check(int n){
for (int i = 0; i < n; i++){
if (array[i] == array[n] || Math.abs(array[i] - array[n]) == Math.abs(i - n)){
return false;
}
}
return true;
}
/**
* 打印數組
*/
private void print(){
for (int i = 0; i < array.length; i++){
System.out.print(array[i]+"\t");
}
System.out.println();
}
}
Go版本
package main
import (
"fmt"
)
//八皇后問題
//記錄打印總次數
var count int = 0
//使用一維數組表示棋盤 第i個元素表示第i個皇后,第i行,值表示該皇后在第幾列
var data []int = make([]int, 8)
func main(){
put(0)
fmt.Println(count)
}
//思路: 首先,需要有一個放置皇后的方法
func put(n int){
//當放置的n爲8時,即爲第9個皇后 因爲數組從0開始
if (n == 8){
fmt.Println(data)
count++
return
}
//如果不是第9個皇后,那麼就遍歷來放置第1到8個皇后
for i := 0; i < 8; i ++{
//先把當前的子n 放在i這個位置上
data[n] = i
//判斷該點是否可以進行落子
if (check(n)){
put(n + 1)
}
}
}
//檢查皇后是否在同一列或者在同一斜線
func check(n int) bool{
for i := 0; i < n; i++{
//第一個 data[i] == data[n] 我們此處用下標代表第幾行 用數值代表第幾列 那麼當第i行的皇后與第n行的皇后在同一列
//就不符合題意了
//第二個 abs(data[n] - data[i]) == abs(i - n) n 和 i代表皇后的橫座標 data[n] data[i]代表皇后的縱座標 那麼兩個皇后橫座標相減 縱座標相減相同時
//其兩個皇后構成了一個與棋盤45°的夾角,棋盤是正方形,所以 此時兩個皇后處在同一斜線上
if (data[i] == data[n] || abs(data[n] - data[i]) == abs(i - n)){
return false
}
}
return true
}
//goLang沒有整數的取絕對值方法 自己寫一個
func abs(n int) int{
if (n < 0){
return -n
}
return n
}
天行健,君子以自強不息,地勢坤,君子以厚德載物。