1. 题目

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

8-queens.png

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.."]
]

2. 思路

回溯的方式求解。第一步将左上顶点置位作为起始点。第一个点一定是有效位。然后每次试探的为下一行找到合适的位置,如果找到了递归的继续找下一行,如果下一行超过了N,则说明得到一个有效解。如果下一行找不到合适的位置,或者是已经找到了有效解,则改为从当前最后一行的当前有效位置的下一个位置开始往后试探,扎到一个新的有效位置。如果在当前行找不到,则回退这一行,从上一行的位置开始往后找。如果一直回退到全部-1行,则全部递归完成。

试探某一个位置是否有效,通过已有的皇后影响的位置来判断。如果[i, j]位置有皇后,从二维坐标平面上考虑,影响的空间实际上是x=i; y=j; x+y=i+j; x-y=i-j四条线段覆盖的所有的点。可以通过四个数组来标记。

3. 代码

耗时:113ms

// 棋盘的横坐标是x,纵坐标是y。
// 横、纵、左斜、右斜四个方向可以用四个一次函数表示
// 对于坐标[i,j ]对应的四条线分别是:
// 横线可以用x=i [0,n-1]
// 纵线是y=j [0,n-1]
// 反斜线是x-y=i-j [-n+1,n-1]
// 正斜线是x+y=i+j [0,2n-2]
// 因此用四个数组可以表示这棋盘上所有被杀点, 在实现时为了方便可以用一个长度为6n-2的数组来存储

class ChessBoard {
public:
    bool* kill;  // x,y,x-y,x+y分别对应区段为[0,n)[n,2n)[2n,4n-1)[4n,6n-1)
    int n;
    ChessBoard(int N) : kill(NULL), n(N) {
        kill = new bool[6*N];
        for (int i = 0; i < 6*N; i++) {
            kill[i] = false;
        }
    }
    ~ChessBoard() {
        delete[] kill;
    }
    
    vector<int> four_lines(int i, int j) {
        vector<int> l;
        l.push_back(i);
        l.push_back(n + j);
        l.push_back(2 * n + i -j + n -1);
        l.push_back(4 * n + i + j);
        return l;
    }
    
    void add(int i, int j) {
        vector<int> pos = four_lines(i, j);
        for (int k = 0; k < 4; k++) {
            kill[pos[k]] = true;
        }
    }
    
    void del(int i, int j) {
        vector<int> pos = four_lines(i, j);
        for (int k = 0; k < 4; k++) {
            kill[pos[k]] = false;
        }
    }
    
    bool valid(int i, int j) {
        vector<int> pos = four_lines(i, j);
        for (int k = 0; k < 4; k++) {
            if (kill[pos[k]]) {
                return false;
            }
        }
        return true;
    }
};

class Solution {
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> ret;
        ChessBoard cb(n);
        vector<int> ans; // 第i个元素存储第i+1行的列号
        ans.push_back(0);
        cb.add(0, 0);
        //cout << "start i=0 j=0" << endl;
        while (!ans.empty()) {
            if (tryNQueens(cb, ans)) {  // 在一个有效的部分布局下填充下一个行
                if (ans.size() == cb.n) {
                    fill(ret, ans);
                    move_next(cb, ans);
                }
            } else {
                move_next(cb, ans);
            }
        }
        return ret;
    }
    
    void move_next(ChessBoard& cb, vector<int>& ans) {
        if (ans.size() == 0) { return ;}
        while (ans.size() != 0) {
            int i = ans.size() - 1;
            int j = ans.back();
            ans.pop_back();
            cb.del(i, j);
            j++;
            for (; j < cb.n; j++) {
                if (cb.valid(i, j)) {
                    ans.push_back(j);
                    cb.add(i, j);
                    //cout << "move next pos: i=" << i << " j=" << j << endl;
                    return ;
                }
            }
            //cout << "rool back row=" << ans.size() << endl;
        }
        return ;
    }
    
    bool tryNQueens(ChessBoard& cb, vector<int>& ans) {
        if (ans.size() == cb.n) {
            return true;
        }
        int i = ans.size();
        for (int j = 0; j < cb.n; j++) {
            if (cb.valid(i, j)) {
                ans.push_back(j);
                cb.add(i, j);
                //cout << "try next row: i=" << i << " j=" << j << endl;
                return true;;
            }
        }
        //cout << "try fail, i=" << i << endl;
        return false;
    }
    
    void fill(vector<vector<string>>& ret, vector<int>& ans) {
        //cout << "[Find]" << endl;
        string temp;
        for (int i = 0; i < ans.size(); i++) {
            temp += ".";
        }
        vector<string> str_ans;
        for (int i = 0; i < ans.size(); i++) {
            string s = temp;
            s[ans[i]] = 'Q';
            str_ans.push_back(s);
            //cout << s << endl;
        }
        ret.push_back(str_ans);
        return ;
    }
    
};

knzeus
72 声望28 粉丝

行万里路,读万卷书