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.
Example:
Input: 4 Output: [ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ] Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.
这里大家需要了解一下国际象棋中皇后的规则
皇后是囯际象棋棋局中实力最强的一种棋子。后可横直斜走,且格数不限。
也就是说要布置一个(n * n)的矩阵,矩阵中n个皇后不能互相吃到即每排 每列 45°斜列 135°斜列 均只能存在一个皇后。
第一题要求算出所有结果并保存起来,第二题只需要求出结果的个数就行了。
所以这个题第一反应基本还是靠递归完成,首先还是申请矩阵,初始化矩阵为全为 '*',如果为'*'表明这个位置是“安全”的,可以塞一个皇后'Q',塞完皇后'Q'之后,对该皇后能“吃到”的位置赋值为 '.',表明这个位置是“危险”的。
很容易发现其实每行只能存在一个皇后,所以递归的方式为直接遍历矩阵。
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> res;
string s(n, '*');
vector<string> record;
for (int i = 0; i < n; i++)
{
record.push_back(s);
}
ifok(res, record, 0, n);
return res;
}
void ifok(vector<vector<string>> &res,vector<string> matrix, int row,int n)
{
if (row == n-1)
{
for (int i = 0; i < n; i++)
{
if (matrix[row][i] == '*')
{
matrix[row][i] = 'Q';
res.push_back(matrix);
return;
}
}
return;
}
for (int i = 0; i < n; i++)
{
if (matrix[row][i] == '*')
{
vector<string> tmp = matrix;
for (int a = 0; a < n; a++)
{
tmp[row][a] = '.';
tmp[a][i] = '.';
}
int a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
tmp[a--][b++] = '.';
}
a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
tmp[a++][b++] = '.';
}
a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
tmp[a--][b--] = '.';
}
a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
tmp[a++][b--] = '.';
}
tmp[row][i] = 'Q';
ifok(res, tmp, row + 1, n);
}
}
}
};
第二题求个数就申请了一个简单的int数组进行处理的。其实跟第一题方法几乎一模一样,也是递归,时间复杂度相同。
class Solution {
public:
int totalNQueens(int n) {
int count = 0;
int **s = new int*[n];
for (int i = 0; i < n; i++)
{
s[i] = new int[n];
memset(s[i], 0, n * sizeof(int));
}
ifok(count, s, 0, n);
return count;
}
void ifok(int &res, int** matrix, int row, int n)
{
if (row == n - 1)
{
for (int i = 0; i < n; i++)
{
if (caniinsert(matrix, row, i, n))
{
res++; return;
}
}
}
for (int i = 0; i < n; i++)
{
if (caniinsert(matrix, row, i, n))
{
matrix[row][i] = 1;
ifok(res, matrix, row + 1, n);
matrix[row][i] = 0;
}
}
return;
}
bool caniinsert(int** tmp, int row,int i, int n)
{
for (int a = 0; a < n; a++)
{
if (tmp[a][i] == 1 || tmp[row][a] == 1)
return false;
}
int a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
if (tmp[a--][b++] == 1)
return false;
}
a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
if (tmp[a++][b++] == 1)
return false;
}
a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
if (tmp[a--][b--] == 1)
return false;
}
a = row, b = i;
while (a < n&&a >= 0 && b < n&&b >= 0)
{
if (tmp[a++][b--] == 1)
return false;
}
return true;
}
};
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
下面是discuss区关于问题2求个数的一份比较简单的代码,我自己研究了半天
首先先考虑我上面递归的算法的本质
以4*4 为例 初始化矩阵
然后我们在第一行插入一个皇后'Q' 同时把皇后能“吃到”的位置置为 '·'
接下来就还是一行一行的往下走,看“安全”的位置(值为'*')插入皇后
最后可以得到结果:
可以看出来,如果矩阵变大,我们每次需要判断更新的位置个数还是挺多的,一共需要更新n*n个位置
那么有没有能够比较简便的方法呢?
看下面:
我们把矩阵左边一列和下面一行拿出来,作为一个大小为2*n-1的向量,设为d1
把矩阵右边一列和下面一行拿出来,作为一个大小为2*n-1的向量设为d2
现在我们再看下之前插入第一个皇后的场景
这次我们只需看两条斜边,即45°斜列和135°斜列,分别指向了d1[1] 和 d2[3]
于是之前对于整个矩阵的更新判断就简化成了对两个向量外加皇后本身所在列向量(设为向量row,大小为n,因为一共有n列)的判断。
皇后位置[i,j]与d1 d2的对应关系为 d1[i+j] d2[i+n-1-j]
最后,贴上这篇代码:
class Solution {
public:
int find(int n, int left, int i,vector<int>&rows, vector<int>&d1, vector<int>&d2) {
if (left == 0)
return 1;
int j, sum = 0;
for (j = 0; j<n; j++) {
if (rows[j] || d1[i + j] || d2[n - 1 + i - j])
continue;
rows[j] = d1[i + j] = d2[n - 1 + i - j] = 1;
sum += find(n, left - 1, i + 1, rows, d1, d2);
rows[j] = d1[i + j] = d2[n - 1 + i - j] = 0;
}
return sum;
}
int totalNQueens(int n) {
vector<int> rows(n),d1(2 * n - 1),d2(2 * n - 1);
return find(n, n, 0, rows, d1, d2);
}
};