Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】

叫我詹躲躲 發表於 2021-12-12
LeetCode

簡約圖文你好2022祝福問候公眾號推送首圖@凡科快圖.png

題目

*n 皇后問題 研究的是如何將 n 個皇后放置在 n × n 的棋盤上,並且使皇后彼此之間不能相互攻擊。
給你一個整數 n ,返回 n 皇后問題 不同的解決方案的數量。*

Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】

皇后走法規則

皇后的走法是:可以橫直斜走,格數不限。因此要求皇后彼此之間不能相互攻擊,等價於要求任何兩個皇后都不能在同一行、同一列以及同一條斜線上。

示例

示例 1:
Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】

輸入:n = 4
輸出:2

解釋:如上圖所示,4 皇后問題存在兩個不同的解法。

示例 2:

輸入:n = 1
輸出:1

提示:
1 <= n <= 9

思路

  1. 定義判斷當前位置的檢驗函式,約束條件包含 ,不能同行,不能同列,不能同對角線(45度和135度)
  2. 定義棋盤;標準回溯處理;

使用回溯的具體做法是:依次在每一行放置一個皇后,每次新放置的皇后都不能和已經放置的皇后之間有攻擊,即新放置的皇后不能和任何一個已經放置的皇后在同一列以及同一條斜線上。當 NNN 個皇后都放置完畢,則找到一個可能的解,將可能的解的數量加 111。

Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】

圖片來源

解題程式碼

var totalNQueens = function (n) {
    let count = 0; //皇后可放置的總數
    let isValid = (row, col, board, n) => {
        //所在行不用判斷,每次都會下移一行
        //判斷同一列的資料是否包含
        for (let i = 0; i < row; i++) {
            if (board[i][col] === 'Q') {
                return false
            }
        }
        //判斷45度對角線是否包含
        for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (board[i][j] === 'Q') {
                return false
            }
        }
        //判斷135度對角線是否包含
        for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; j--, i--) {
            if (board[i][j] === 'Q') {
                return false
            }
        }
        return true
    }

    let backTracing = (row, board) => {
        //走到最後一行,統計次數
        if (row === n) {
            count++;
            return
        }

        for (let x = 0; x < n; x++) {
            //判斷該位置是否可以放置 皇后
            if (isValid(row, x, board, n)) {
                board[row][x] = 'Q'; //放置皇后
                backTracing(row + 1, board); //遞迴
                board[row][x] = '.'; //回溯,撤銷處理結果
            }
        }
    }
    backTracing(0, board)
    let board = [...Array(n)].map(v => v = ([...Array(n)]).fill('.')) //棋盤
    return count
};

總結

主要運用了回溯演算法;而解決一個回溯問題,實際上就是一個決策樹的遍歷過程。

let backtracking=(路徑,選擇列表) =>{
    if (滿足結束條件)) {
        存放路徑;
        return;
    }
    for (選擇:路徑,選擇列表) {
        做出選擇;
        backtracking(路徑,選擇列表); // 遞迴
        回溯,撤銷處理結果
    }
}

即:

  1. 1.路徑:也就是已經做出的選擇。
  2. 2.選擇列表:也就是你當前可以做的選擇。
  3. 3.結束條件:也就是到達決策樹底層,無法再做選擇的條件。

剪枝函式

  1. 1.用約束條件剪除得不到的可行解的子樹
  2. 2.用目標函式剪取得不到的最優解的子樹

回溯法的一般步驟:

  1. 1.設定初始化的方案(給變數賦初始值,讀入已知資料等)
  2. 2.變換方式去試探,若全部試完側轉(7)
  3. 3.判斷此法是否成功(通過約束函式),不成功則轉(2)
  4. 4.試探成功則前進一步再試探
  5. 5.正確方案還是未找到則轉(2)
  6. 6.以找到一種方案則記錄並列印
  7. 7.退回一步(回溯),若未退到頭則轉(2)
  8. 8.已退到頭則結束或列印無解

每天精進,繼續加油!