經典算法(21)畢業生求職必會算法 八皇后問題

寫在前面: 我是 「揚帆向海」,這個暱稱來源於我的名字以及女朋友的名字。我熱愛技術、熱愛開源、熱愛編程。技術是開源的、知識是共享的

這博客是對自己學習的一點點總結及記錄,如果您對 Java算法 感興趣,可以關注我的動態,我們一起學習。

用知識改變命運,讓我們的家人過上更好的生活

相關文章

點此查看 【算法系列】 博客文章


一、問題描述

八皇后問題,一個古老而著名的問題,是回溯算法的典型案例。該問題由國際西洋棋棋手馬克斯·貝瑟爾於 1848 年提出:在 8×8 格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。高斯認爲有 76種方案。1854 年在柏林的象棋雜誌上不同的作者發表了 40 種不同的解,後來有人用圖論的方法解出 92種結果。計算機發明後,有多種計算機語言可以編程解決此問題。

相信大家都玩過 死亡八皇后遊戲,下面是我玩在線遊戲錄的屏。

在這裏插入圖片描述

二、問題分析

目的:8×8 格的國際象棋上擺放八個皇后

規則:任意兩個皇后都不能處於同一行、同一列或同一斜線上

由於任意皇后不能同行,因此每一行最多放一個皇后;
由於行數等於皇后數,因此每一行至少要一個皇后。

結論每一行一定是放一個皇后

三、實現邏輯

在這裏插入圖片描述

① 首先把第一個皇后放在 8×8 格國際象棋的第1行第1列

② 接下來放第二個皇后,嘗試放在第二行第一列,然後判斷是否滿 足規則,如果不滿足,繼續嘗試放在第二列、第三列…嘗試把所有的列的位置全部放完,找到一個合適的放置位置。

③ 繼續放第三個皇后,放置過程和第②步同理。

④ 依次類推,放第四個、第五個、第六個、第七個、第八個皇后

⑤ 通過以上過程得到一個正確的擺放方案時:

  1. 嘗試把第八個皇后移動擺放位置,看有沒有其它擺放方式;
  2. 嘗試把第七個皇后移動擺放位置,看有沒有其它擺放方式;
  3. 嘗試把第六個皇后移動擺放位置,看有沒有其它擺放方式;
  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 種擺放方案,由於篇幅有些,在此只截取了兩種方案。

在這裏插入圖片描述

在這裏插入圖片描述


由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章