[LeetCode] N-Queens

N-Queens

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

For example,
There exist two distinct solutions to the 4-queens puzzle:

[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

解題思路:

N皇后問題。基本思想是回溯法。

解法1:

naive的辦法,每次確定一行的位置,檢查與已有行的列、撇對角線、捺對角線是否有衝突。

class Solution {
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> result;
        if(n<=0){
            return result;
        }
        vector<string> item;
        
        helper(result, item, n);
        
        return result;
    }
    
    void helper(vector<vector<string>>& result, vector<string>& item, int n){
        if(item.size() == n){
            result.push_back(item);
            return;
        }
        for(int i = 0; i<n; i++){
            if(check(item, i, n)){
                string s(n, '.');
                s[i]='Q';
                item.push_back(s);
                helper(result, item, n);
                item.pop_back();
            }
        }
    }
    
    //當前行擺放在第col列中,與驗證已有的擺放是否合法
    bool check(vector<string>& item, int col, int n){
        int len = item.size();
        //驗證列是否合法
        for(int i=0; i<len; i++){
            if(item[i][col] == 'Q'){
                return false;
            }
        }
        //驗證捺對角線是否合法
        for(int i = len-1, j = col - 1; i>=0 && j>=0; i--, j--){
            if(item[i][j] == 'Q'){
                return false;
            }
        }
        //驗證撇對角線是否合法
        for(int i = len-1, j = col + 1; i>=0 && j<n; i--, j++){
            if(item[i][j] == 'Q'){
                return false;
            }
        }
        return true;
    }
};
解法2:

上述相當於用一個二維數組表示棋盤的狀態。可以用一個一維數組表示棋盤的狀態,a[i]表示第i行棋的位置。

class Solution {
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> result;
        if(n<=0){
            return result;
        }
        vector<string> item;
        vector<int> row;
        
        helper(result, item, row, n);
        
        return result;
    }
    
    void helper(vector<vector<string>>& result, vector<string>& item, vector<int>& row, int n){
        if(item.size() == n){
            result.push_back(item);
            return;
        }
        for(int i = 0; i<n; i++){
            if(check(i, row)){
                string s(n, '.');
                s[i]='Q';
                item.push_back(s);
                row.push_back(i);
                helper(result, item, row, n);
                row.pop_back();
                item.pop_back();
            }
        }
    }
    
    //當前行擺放在第col列中,與驗證已有的擺放是否合法
    bool check(int col, vector<int>& row){
        int len = row.size();
        
        for(int i=0; i<len; i++){
            if(row[i]==col || abs(row[i]-col) == abs(len-i)){
                return false;
            }
        }
        
        return true;
    }
};
解法3:

一個比較talent的辦法是,用一個整數的不同位來表示所有有衝突的位置。其代碼如下:

class Solution {
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> result;
        if(n<=0){
            return result;
        }
        vector<string> item;
        
        helper(result, item, n, 0, 0, 0);
        
        return result;
    }
    
    void helper(vector<vector<string>>& result, vector<string>& item, int n, int d, int l, int r){
        if(item.size() == n){
            result.push_back(item);
            return;
        }
        int upperlimit = (1 << n) - 1;
        int p = upperlimit & ~(d | l | r);
        int i = 0;
        while(p){
            if(p&1){
                string s(n, '.');
                s[i] = 'Q';
                item.push_back(s);
                int m = 1 << i;
                helper(result, item, n, d | m, (l | m)<<1, (r | m) >> 1);
                item.pop_back();
            }
            i++;
            p = p>>1;
        }
    }
};
參數d的二進制1位表示已放的行中列的衝突位置。l表示已放的行中撇的衝突位置,r表示捺的衝突位置。d | l | r表示所有的衝突位置。upperlimit表示所有的有效位,其二進制表示爲n個1。p = upperlimit & (d|l|r)表示所有可放的位置。再進入下一層調用時,我們確定當前可放的位爲從右往左數的第i位,那麼d應該設爲d | (1<<i),這個容易理解。l應該設爲(l | (1<<i)) << 1,因爲下一行撇對角線相對於當前的不可用往左移一位。同樣,r應該改爲(r|(1<<i))>>1.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章