n 皇后问题 研究的是如何将 n
个皇后放置在 n × n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4 输出:2 解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1 输出:1
提示:
1 <= n <= 9
步骤1:定义问题性质
计算问题性质:
-
问题描述:n 皇后问题是指在 n x n 的棋盘上放置 n 个皇后,使得任何两个皇后都不能相互攻击。
-
输入条件:一个整数 n,表示棋盘的大小和皇后数量。
-
输出条件:返回不同解决方案的数量。
-
限制条件:
-
棋盘大小 n 在 1 到 9 之间。
-
-
潜在边界条件:
-
当 n=1 时,唯一解是将皇后放在 (1,1) 位置。
-
当 n=2 或 n=3 时,没有解。
-
步骤2:分解问题与算法选择
分解步骤:
-
初始化:开始于第一行,尝试将皇后放置在该行的每一列。
-
递归检查:对于每一行,尝试将皇后放在每一列,并检查是否与之前放置的皇后冲突(同一列、同一对角线)。
-
回溯:如果当前放置导致冲突,尝试下一列。如果所有列都尝试过,回溯到上一行。
-
解的计数:当完成所有行且没有冲突时,增加解的计数。
最佳算法设计思路:
-
回溯法:这是解决 n 皇后问题的最佳方法。回溯法通过尝试所有可能性但在发现无解时迅速回溯,减少了不必要的计算。
-
时间复杂度:O(N!), 因为需要检查每一种可能的排列,但由于剪枝,实际运行时间会远低于此。
-
空间复杂度:O(N) 用于存储每个皇后的位置或状态。
-
步骤3:C++实现
class Solution {
private:
int count = 0; // 用于计数有效的解
// 检查是否可以放置皇后
bool isValid(int row, int col, const std::vector<int>& queens) {
for (int i = 0; i < row; ++i) {
if (queens[i] == col ||
std::abs(queens[i] - col) == std::abs(i - row)) {
return false;
}
}
return true;
}
// 递归求解
void solveNQueensHelper(int n, int row, std::vector<int>& queens) {
if (row == n) {
++count;
return;
}
for (int col = 0; col < n; ++col) {
if (isValid(row, col, queens)) {
queens[row] = col; // 尝试放置皇后
solveNQueensHelper(n, row + 1, queens);
}
}
}
public:
int totalNQueens(int n) {
std::vector<int> queens(n, -1); // 记录每一行皇后的位置
solveNQueensHelper(n, 0, queens);
return count;
}
};
步骤4:启发
-
回溯法的应用可以帮助解决许多排列组合和约束满足问题,教导我们如何在复杂问题中寻找有效解。
-
剪枝策略的重要性,通过提前判断无效状态减少了计算量。
-
问题抽象:将棋盘问题抽象为位置数组,简化了问题处理。
步骤5:实际生活中的应用
应用场景:
-
任务调度:在多任务操作系统或项目管理中,将任务视为皇后,时间片或资源视为棋盘格,保证任务之间无冲突。
具体实现:
-
资源分配:在一个大型IT项目中,假设有多个开发团队(皇后)需要使用同一套服务器资源(棋盘)。我们可以使用n皇后算法来安排不同团队的服务器使用时间,确保在同一时间段内没有两个团队使用相同的服务器资源,从而避免资源冲突。
-
实现方法:
-
定义一个虚拟的“时间棋盘”,每行代表一天,每列代表一个服务器。
-
使用回溯法尝试分配每天的服务器使用,确保同一服务器在一天内不被多个团队使用(同列冲突),不同天使用同一服务器的团队不重叠(对角线冲突)。
-
在解决方案中,每个团队的分配可以看作是解决方案的一个“解”,通过这种方式可以优化资源的使用效率,减少项目延误和资源冲突。
-
-