N 皇后 II
n 皇后问题 研究的是如何将
n
个皇后放置在n × n
的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数
n
,返回 n 皇后问题 不同的解决方案的数量。
思路:
全排列,验证在各个行放置不同列的可行性
递归函数:返回从i行往后的有效解决方案的数量。
如何判断某位置有效?
- 利用容器接收之前已放入的全部位置,根据规则:不能同列(y!=vec[i]);不能在同一斜对角线(abs(x-i)==abs(y-x_y[i]));不能同行(遍历时自己控制)
大体流程:
basecase:
- i==N(所有行都已放置过棋子),返回1
recur:
- 遍历第i行的所有列,若位置有效,则进入下一行并将已填入的pos放入容器中
代码:
class Solution {
public:
int N;
bool is_valid(int x, int y, vector<int>& x_y) {
for (int i = 0; i < x; ++i) {
if (x_y[i] == y || abs(x - i) == abs(y - x_y[i]))
return false;
}
return true;
}
int f(int i, vector<int>& x_y) {
if (i == N)
return 1;
int cnt = 0;
for (int k = 0; k < N; ++k) {
if (is_valid(i, k, x_y)) {
x_y[i] = k;
cnt += f(i + 1, x_y);
//x_y[i] = 0;//为什么不需要?判断i和k有效与[i]无关,若有效,后续会覆盖掉
}
}
return cnt;
}
int totalNQueens(int n) {
N = n;
vector<int> x_y(n);
return f(0, x_y);
}
};
优化思路:
优化判断有效的过程(多变量):
- limit:最终的结果,全部列都被占据(利用二进制的形式,对于占据的列用1表示)
- col:记录已经占据过的列(二进制形式)
- left:记录因为‘/对角线’对该行造成的限制
- right:记录因为‘\对角线’对该行造成的限制
- 无效ban:无效的全部列;col | left | right
- 有效valid:有效的全部列;limit&(~ban)
优化遍历的次数,提取所有的有效pos:
- pos:该行可行的列 valid&(~valid+1)
- valid的更新:valid^pos
大体流程与思路1一致
优化代码:
class Solution {
public:
int limit;
int f(int col, int left, int right) {
if (col == limit)
return 1;
int cnt = 0;
int ban = col | left | right;
int valid = limit & ~ban;
int pos;
while (valid) {
pos = valid & (~valid + 1);
valid ^= pos;
cnt += f(col | pos, (left | pos) >> 1, (right | pos) << 1);
}
return cnt;
}
int totalNQueens(int n) {
limit = (1 << n) - 1;
int row = 0, col = 0;
return f(0, 0, 0);
}
};