回溯算法的經典應用 —— LeetCode 51.N皇后問題(Kotlin)

head

separatorLine

題目描述

N皇后 ← 點擊鏈接進入題目

n 皇后問題研究的是如何將 n 個皇后放置在 n×n 的棋盤上,並且使皇后彼此之間不能相互攻擊。

上圖爲 8 皇后問題的一種解法。

給定一個整數 n,返回所有不同的 n 皇后問題的解決方案。

每一種解法包含一個明確的 n 皇后問題的棋子放置方案,該方案中 ‘Q’ 和 ‘.’ 分別代表了皇后和空位。

示例:

輸入: 4
輸出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解釋: 4 皇后問題存在兩個不同的解法。

解決方案

class Solution {
    // 記錄總皇后數
    private var totalQueues = 0
    // 記錄已經放置的皇后位置
    private lateinit var queens: IntArray
    // 記錄可行的方案
    private lateinit var solutions: MutableList<List<String>>

    fun solveNQueens(n: Int): List<List<String>> {
        solutions = mutableListOf()
        totalQueues = n
        queens = IntArray(n)
        backTrack(0)
        return solutions
    }

    /**
     * 放置第 current 個皇后
     */
    private fun backTrack(current: Int) {
        // 如果已經放下了最後一個皇后,表明此方案可行,記錄此方案
        if (current == totalQueues) {
            val queenList = mutableListOf<String>()
            for (i in 0 until totalQueues) {
                val stringBuilder = StringBuilder()
                for (j in 0 until totalQueues)
                    stringBuilder.append(if (queens[i] == j) "Q" else ".")
                queenList.add(stringBuilder.toString())
            }
            solutions.add(queenList)
            return
        }
        // 還未到最後一行,嘗試放置皇后
        for (position in 0 until totalQueues) {
            queens[current] = position
            // 如果能放在此位置,繼續放置下一個皇后
            if (canPlace(current)) backTrack(current + 1)
            // 否則繼續循環,嘗試放置在另一個位置。這裏是回溯的精髓,有一個回到上一步的操作
        }
    }

    /**
     * 判斷皇后能否放在此位置
     */
    private fun canPlace(position: Int): Boolean {
        // 判斷是否與之前放置的皇后衝突
        for (before in 0 until position) {
            if (queens[before] == queens[position]
                || Math.abs(queens[position] - queens[before]) == Math.abs(position - before)
            )
                return false
        }
        return true
    }

解題思路

1.用queens[current]=position表示第current個皇后放在第current行,第position列。

2.由於每一行上只有一個皇后,所以皇后在橫向不會衝突。

3.先嚐試將皇后放在position位置上,再判斷這個皇后放在這裏會不會和之前的皇后衝突。判斷方法爲:

if (queens[before] == queens[position] 
|| Math.abs(queens[position] - queens[before]) == Math.abs(position - before))
                return false

(1)如果queens[before] == queens[position],表示豎向衝突

(2)如果Math.abs(queens[position] - queens[before]) == Math.abs(position - before)),表示斜線上衝突。

4.如果這個皇后沒有和之前放置的所有皇后衝突,表示此皇后可以放在這裏,繼續放下一個皇后。

5.如果這個皇后和之前放置的皇后衝突,則回到上一步,嘗試將此皇后放置在下一個position位置上。這個回到上一步的操作就是回溯算法的精髓。

6.如果放完所有皇后都沒有衝突,表示此方案可行。根據queens數組拼接答案即可。

參考文章:回溯算法經典應用之—N皇后問題 (Java):https://blog.csdn.net/qianhaifeng2012/article/details/52300829

separatorLine

推薦閱讀:
LeetCode題解,使用kotlin語言,歡迎star:

注:本文排版仿照LeetCode官方公衆號,侵刪

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