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.