开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
52. N皇后 II
难度困难404
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入: n = 4
输出: 2
解释: 如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入: n = 1
输出: 1
直观的做法是暴力枚举将 NN 个皇后放置在 N \times NN×N 的棋盘上的所有可能的情况,并对每一种情况判断是否满足皇后彼此之间不相互攻击。暴力枚举的时间复杂度是非常高的,因此必须利用限制条件加以优化。
显然,每个皇后必须位于不同行和不同列,因此将 NN 个皇后放置在 N \times NN×N 的棋盘上,一定是每一行有且仅有一个皇后,每一列有且仅有一个皇后,且任何两个皇后都不能在同一条斜线上。基于上述发现,可以通过回溯的方式得到可能的解的数量。
回溯的具体做法是:依次在每一行放置一个皇后,每次新放置的皇后都不能和已经放置的皇后之间有攻击,即新放置的皇后不能和任何一个已经放置的皇后在同一列以及同一条斜线上。当 NN 个皇后都放置完毕,则找到一个可能的解,将可能的解的数量加 11。
由于每个皇后必须位于不同列,因此已经放置的皇后所在的列不能放置别的皇后。第一个皇后有 NN 列可以选择,第二个皇后最多有 N-1N−1 列可以选择,第三个皇后最多有 N-2N−2 列可以选择(如果考虑到不能在同一条斜线上,可能的选择数量更少),因此所有可能的情况不会超过 N!N! 种,遍历这些情况的时间复杂度是 O(N!)O(N!)。
为了降低总时间复杂度,每次放置皇后时需要快速判断每个位置是否可以放置皇后,显然,最理想的情况是在 O(1)O(1) 的时间内判断该位置所在的列和两条斜线上是否已经有皇后。
以下两种方法分别使用集合和位运算对皇后的放置位置进行判断,都可以在 O(1)O(1) 的时间内判断一个位置是否可以放置皇后,算法的总时间复杂度都是 O(N!)O(N!)。
class Solution {
public:
int m;
int ret[11][11];
bool judge(int row, int col) {
for (int i = 0; i < row; i++) {if (ret[i][col] == 1) return false;}
for (int i = 0; i < col; i++) {if (ret[row][i] == 1) return false;}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (ret[i][j] == 1) return false;}
for (int i = row - 1, j = col + 1; i >= 0 && j < m; i--, j++) {if (ret[i][j] == 1) return false;}
return true;
}
int cnt = 0;
void dfs(int row) {
if (row == m) {cnt++; return;}
for (int col = 0; col < m; col++) {
if (judge(row, col)) {
ret[row][col] = 1;
dfs(row + 1);
ret[row][col] = 0;
}
}
}
int totalNQueens(int n) {
memset(ret, 0, sizeof(ret));
m = n;
dfs(0);
return cnt;
}
};