寫在前面: 我是 「揚帆向海」,這個暱稱來源於我的名字以及女朋友的名字。我熱愛技術、熱愛開源、熱愛編程。
技術是開源的、知識是共享的
。
這博客是對自己學習的一點點總結及記錄,如果您對 Java、算法 感興趣,可以關注我的動態,我們一起學習。
用知識改變命運,讓我們的家人過上更好的生活
。
相關文章
一、問題描述
八皇后問題
,一個古老而著名的問題,是回溯算法的典型案例。該問題由國際西洋棋棋手馬克斯·貝瑟爾於 1848 年提出:在 8×8 格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上
,問有多少種擺法。高斯認爲有 76種方案。1854 年在柏林的象棋雜誌上不同的作者發表了 40 種不同的解,後來有人用圖論的方法解出 92種結果。計算機發明後,有多種計算機語言可以編程解決此問題。
相信大家都玩過 死亡八皇后遊戲
,下面是我玩在線遊戲錄的屏。
二、問題分析
目的:8×8 格的國際象棋上擺放八個皇后
規則:任意兩個皇后都不能處於同一行、同一列或同一斜線上
由於任意皇后不能同行,因此每一行最多放一個皇后;
由於行數等於皇后數,因此每一行至少要一個皇后。
結論: 每一行一定是放一個皇后
三、實現邏輯
① 首先把第一個皇后放在 8×8 格國際象棋的第1行第1列
② 接下來放第二個皇后,嘗試放在第二行第一列,然後判斷是否滿 足規則,如果不滿足,繼續嘗試放在第二列、第三列…嘗試把所有的列的位置全部放完,找到一個合適的放置位置。
③ 繼續放第三個皇后,放置過程和第②步同理。
… …
④ 依次類推,放第四個、第五個、第六個、第七個、第八個皇后
⑤ 通過以上過程得到一個正確的擺放方案時:
- 嘗試把第八個皇后移動擺放位置,看有沒有其它擺放方式;
- 嘗試把第七個皇后移動擺放位置,看有沒有其它擺放方式;
- 嘗試把第六個皇后移動擺放位置,看有沒有其它擺放方式;
… …
- 依次類推,把第五個、第四個、第三個、第二個皇后移動位置,看有沒有其它擺放方式。
這樣就得到了把第一個皇后放在第一行第一列所有可能性
⑥ 然後回頭將第一個皇后放到第二列,繼續循環執行上面的步驟 ①、②、③ 、④ 、⑤
在此過程中使用到了
回溯算法
的思想,從一條路往前走,能進則進,不能進則退回來,換一條路再試。
八皇后問題就是回溯算法的典型,第一步按照順序放一個皇后,然後第二步符合要求放第2個皇后,如果沒有位置符合要求,那麼就要改變第一個皇后的位置,重新放第2個皇后的位置,直到找到符合條件的位置就可以了。
回溯在 迷宮搜索問題 中使用很常見,就是這條路走不通,然後返回到前一個路口,繼續走下一條路。
四、擺放圖示
在這裏只演示把第一個皇后放在第一行第一列的情況:
第一種:
第二種:
第三種:
第四種:
五、代碼實現
使用一個一維數組表示皇后的位置,其中數組的下標表示皇后所在的行,也就是第幾個皇后,數組元素的值表示皇后所在的列。
package com.study.algorithm;
/**
* @Description:
* @Author: 揚帆向海
* @Date: Created in 01:26 2020/4/4
*/
public class EightQueens {
/**
* 常量值COUNT表示皇后個數
*/
static final int COUNT = 8;
/**
* 用一維數組存放皇后的擺放位置
*/
static int[] array = new int[COUNT];
/**
* 用來記錄有多少種擺放方案
*/
static int sum = 0;
public static void main(String[] args) {
putQueen(0);
System.out.println("八皇后總共有" + sum + "種擺放方案");
}
/**
* 在棋盤上擺放皇后
*
* @param n 第幾個皇后
*/
public static void putQueen(int n) {
// 如果n=COUNT,表示皇后放置完畢
if (n == COUNT) {
System.out.print((sum + 1) + "、八皇后的擺放位置是:");
for (int i = 0; i < COUNT; i++) {
int pos = array[i] + 1;
System.out.print(pos + " ");
}
System.out.println();
System.out.print("擺放位置如下圖所示:");
printPlace();
return;
} else {
// 依次往棋盤中放入皇后
for (int i = 0; i < COUNT; i++) {
// 先把當前這個皇后n,放到該行的第一列
array[n] = i;
// 調用方法,判斷把第n個皇后在第i列時,是否有衝突
if (checkPlace(n)) {
// 不衝突,接着放置第(n+1)個皇后,即開始遞歸
putQueen(n + 1);
}
}
}
}
/**
* 繪製COUNT×COUNT棋盤,打印皇后的位置
*/
public static void printPlace() {
System.out.println();
sum++;
for (int i = 0; i < COUNT; i++) {
System.out.print(" ");
for (int j = 0; j < COUNT; j++) {
System.out.print("---");
}
System.out.println();
for (int k = 0; k < COUNT; k++) {
if (k == array[i]) {
System.out.print("|" + "♛");
} else {
System.out.print("| " + " ");
}
}
System.out.println("|");
}
System.out.print(" ");
for (int i = 0; i < COUNT; i++) {
System.out.print("---");
}
System.out.println();
}
/**
* 檢查皇后的擺放位置是否有衝突
*
* @param n 表示第幾個皇后
* @return
*/
public static boolean checkPlace(int n) {
for (int i = 0; i < n; i++) {
// 一維數組的值表示該行的列值,如果值相同,則表示在同一列
// n-i表示兩個皇后相差幾行,array[n]-array[i]表示相差幾列,如果相減的絕對值相等,則表示在對角線上
if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
}
代碼執行結果:
總共有 92 種擺放方案,由於篇幅有些,在此只截取了兩種方案。
由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!