八皇后問題可以說是一道相當經典的演算法題目,以西洋棋為背景,如何在一個 8x8 的棋盤上擺放八個皇后的棋子,讓任何一個皇后無法吃掉其中一個皇后,也是就是任何一個皇后的橫行、縱行、斜線 上都不會出現其它的皇后,後來八皇后問題也被衍生為 N 皇后問題 — 在 N*N 的棋盤上放置 N 個皇后,試問有幾種解法?就讓我們用回溯法來解 N 皇后的問題吧!
先用四皇后來推演一下流程
假設現在有個 4*4 的棋盤,我們先將第一個皇后放在第一個格子裡面
2. 由於一個直行只能有一個皇后,所以接下來將第二個皇后放在第二行的第一個,不過因為皇后的橫列不可以有其它皇后,所以往下挪動一格
3. 真不巧,斜線上也不能出現其它皇后,因此要再往下挪動一格
4. 因此第二個皇后擺放在這個位置
5. 接下來放置第三個皇后,但會發現一個問題,不管放在哪個位置都不行,因次要使用回溯法來調整第二個皇后的位置,把他往下移動一格
6. 調整完之後第三個皇后也放好了
7. 不過在擺放第四個皇后的時候,又發生了一樣的問題,只好用回溯法再次倒回去
8. 重覆先前的步驟之後,總算能夠成功擺放四個皇后了!
用 js 實作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 const NQueens = (n ) => { let answer = 0 ; let arr = []; for (let i = 0 ; i < n; i++) { arr.push (Array .from ({ length : n }).fill (null )); } let i = 0 ; let j = 0 ; let loop = true ; while (loop) { console .log ("i" , i, "j" , j); arr[i][j] = "Q" ; console .log ("arr" , arr); let checkQExist = false ; let k = 0 ; while (k < i) { if (arr[k][j] === "Q" ) { checkQExist = true ; } k++; } k = 0 ; while (k < j) { if (arr[i][k] === "Q" ) { checkQExist = true ; } k++; } k = 1 ; let l = -1 ; while (i + k < n && j + l >= 0 ) { if (arr[i + k][j + l] === "Q" ) { checkQExist = true ; } k++; l--; } k = -1 ; while (i + k >= 0 && j + k >= 0 ) { if (arr[i + k][j + k] === "Q" ) { checkQExist = true ; } k--; } if (!checkQExist) { console .log ("Q can put here" ); console .log (arr); if (j === n - 1 ) { answer++; console .log ("perfect solution" ); console .log (arr); arr[i][j] = null ; i++; } else { i = 0 ; j++; } } if (checkQExist) { console .log ("Q exist!!" ); console .log (arr); arr[i][j] = null ; i++; } const checkBoundary = ( ) => { j--; for (let r = 0 ; r < arr.length ; r++) { if (arr[r][j] === "Q" ) { arr[r][j] = null ; console .log ("r and j is" , r, j); i = r + 1 ; break ; } } }; while (i >= n) { checkBoundary (); if (j < 0 ) { console .log ("done" ); loop = false ; break ; } } } console .log ("Number of solution" , answer); }; NQueens (4 );
單看程式碼實在是過於抽象,為了幫助理解所以將陣列印出來看,就會比較清楚整體的運作流程為何
為了驗證程式碼是否正確,將 input 改為 8,結果運算時間出乎我的意料,真的是所謂的讓子彈飛一會,非常的耗時,一度還讓我以為是否寫了無窮迴圈 😂,還好最後成功印出 92