1. 程式人生 > >【lintcode】N皇后問題

【lintcode】N皇后問題

n皇后問題是將n個皇后放置在n*n的棋盤上,皇后彼此之間不能相互攻擊。
給定一個整數n,返回所有不同的n皇后問題的解決方案。
每個解決方案包含一個明確的n皇后放置佈局,其中“Q”和“.”分別表示一個女王和一個空位置。

樣例
對於4皇后問題存在兩種解決的方案:
[".Q..", // Solution 1

 "...Q",

 "Q...",

  "..Q."],

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

 "Q...",

  "...Q",

   ".Q.."]

思路:

  • 國際象棋中皇后可以攻擊同行同列以及同一斜線上的棋子,所以要以此來判斷在一個棋盤格上能否放置一個皇后。我們使用一個長度為n的一維陣列Queens
    來儲存每一行的皇后的位置(-1表示沒有皇后)。對於位置(r, c),只需判斷Queens[i] >= 0 && (Queens[i] == c || abs(r-i) == abs(c-Queens[i])) ,若為真表示有衝突,不可放置。
  • 求解過程使用回溯法,(r,c)表示當前位置 對於該位置有如下幾種情況:
    1.r == n-1
     a.  c == n-1(即行尾), 如果有解,則記錄這個解,然後回溯到上一行的皇后之後的位置(同時刪去關於該皇后的記錄);否則直接回溯到上一行
     b. 未到行尾,如果有解,則記錄這個解,然後清除該行的記錄,最後將c加1,移動到下一個位置;否則直接移動到下一個位置
     
    2.r < n-1(即未到最後一行)
     a

    . c < n-1,即沒有到行尾 如果可以放置皇后則記錄相應的位置,然後令 r = r + 1, c = 0 ,移動到下一行; 否則將c加1,移動到下一個位置
     b.c == n-1,即到達行尾 如果可以放置皇后,則記錄,然後移動到下一行;否則,則回溯到上一行的皇后所在位置的後一個位置

    3.在回溯的過程中可能會出現c >= n的情況,這時候如果 r == 0,則函式直接返回;否則回溯到上一行

程式碼

第一次寫的題解中核心函式solve太過複雜,下面先提前給出一個簡化過的solve函式。

    void solve2(int r){
        if(r == n){
            for
(int i = 0; i < n; i++) result[i][Queens[i]] = 'Q'; results.push_back(result); for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) result[i][j] = '.'; } else{ for(int i = 0; i < n; i++){ for(int j = r; j < n; j++) Queens[j] = -1; if(check(r, i)){ Queens[r] = i; solve2(r+1); } } } }

完整題解如下


class Solution {
    vector<int> Queens;
    vector<vector<string> > results;
    vector<string> result;
    int n;
public:
    /*
     * @param n: The number of queens
     * @return: All distinct solutions
     */
    vector<vector<string> > solveNQueens(int _n) {
        // write your code here
        n = _n;
        Queens.resize(n, -1);
        result.resize(n);
        for(int i = 0; i < n; i++)
            result[i].resize(n, '.');

        if(n == 1){
            result[0][0] = 'Q';
            results.push_back(result);
            return results;
        }
        else if(n < 4)
            return results;

        solve(0,0);
        return results;
    }

    void solve(int r, int c){
        if(c  >= n)
            if(r == 0)
                return;
            else{
                r--;
                //刪去回溯點的解
                result[r][Queens[r]] = '.'; 
                int t = Queens[r];
                Queens[r] = -1;
                solve(r, t+1); //回溯到上一行
                return;
            }
        if(r == n-1){
            for( ; c < n; c++){
                if(check(r,c)){
                    result[r][c] = 'Q';
                    results.push_back(result); //新增新解
                    result[r][c] = '.'; //刪去本行的皇后
                }
                if(c == n-1){ 
                    //若已到行尾,則回溯
                    r--;
                    //刪去回溯點的解
                    result[r][Queens[r]] = '.'; 
                    int t = Queens[r];
                    Queens[r] = -1;
                    solve(r, t+1); //回溯到上一行
                    return;
                }
            }
        }
        else{
            for( ; c < n; c++){
                if(check(r,c)){
                    result[r][c] = 'Q';
                    Queens[r] = c;
                    r++;
                    solve(r,0);
                    return;
                }
                if(c == n-1){
                    if(r == 0)
                        return; //如果已經回溯到了第一行,則返回
                    else{
                        r--;
                        //刪去回溯點的解
                        result[r][Queens[r]] = '.'; 
                        int t = Queens[r];
                        Queens[r] = -1;
                        solve(r, t+1); //回溯到上一行
                        return;
                    }
                }
            }
        }
    }

    int check(int r, int c){
        for(int i = 0; i < Queens.size(); i++)
            if(Queens[i] >= 0 && (Queens[i] == c || abs(r-i) == abs(c-Queens[i])) ) 
                return 0;
        return 1;
    }
};