N皇后问题思路

皇后的走法是:可以横直斜走,格数不限。因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。

直观的做法是暴力枚举将 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!)。

 

 

为了判断一个位置所在的列和两条斜线上是否已经有皇后,使用三个集合 \textit{columns}columns、\textit{diagonals}_1diagonals
1

和 \textit{diagonals}_2diagonals
2

分别记录每一列以及两个方向的每条斜线上是否有皇后。

列的表示法很直观,一共有 NN 列,每一列的下标范围从 00 到 N-1N−1,使用列的下标即可明确表示每一列。

如何表示两个方向的斜线呢?对于每个方向的斜线,需要找到斜线上的每个位置的行下标与列下标之间的关系。

方向一的斜线为从左上到右下方向,同一条斜线上的每个位置满足行下标与列下标之差相等,例如 (0,0)(0,0) 和 (3,3)(3,3) 在同一条方向一的斜线上。因此使用行下标与列下标之差即可明确表示每一条方向一的斜线。

方向二的斜线为从右上到左下方向,同一条斜线上的每个位置满足行下标与列下标之和相等,例如 (3,0)(3,0) 和 (1,2)(1,2) 在同一条方向二的斜线上。因此使用行下标与列下标之和即可明确表示每一条方向二的斜线。

每次放置皇后时,对于每个位置判断其是否在三个集合中,如果三个集合都不包含当前位置,则当前位置是可以放置皇后的位置。

posted @ 2020-10-17 14:06  代码非马  阅读(456)  评论(0)    收藏  举报