本篇文章源自“讲算法的熊船长”B站直播节目和公众号文章。(如转载,请将此声明和内容一并转载)
8皇后问题
在国际象棋中,棋盘有8行8列共64个格子,皇后可以走直线和斜线(米字形),8皇后问题是在棋盘上放8个皇后,要求所有皇后都不会互相攻击。
N皇后问题
N皇后问题是8皇后问题的扩展版,即给一个N*N的棋盘,要求放置N个皇后。
DFS
N皇后问题是一个很适合用计算机求解的问题,学习过编程的朋友应该都遇到过。求解N皇后问题可以用一个很经典的算法,深度优先搜索算法(Depth First Search),或者叫做回溯算法(backtracking),很多朋友搞不清这两个算法是什么关系,其实这两个算法是同一回事。
我们在直播中用了超过10种方法来求解这道题(Leetcode 51),下面是其中的一个版本,这个版本只有7行代码。计算机科学家高纳德曾说,计算机程序=数据结构+算法,选择适当的数据结构会让算法变得简洁。面对不同的具体问题,如何选择数据结构和算法,这需要思考和很多练习。
# Leetcode 51
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def sch(qlst: List[int], x: int, y: int, z: int, qs: List[int]) -> List[List[str]]:
if len(qlst) == n:
return [[''.join(['Q' if (1<<i) & q else '.' for i in range(n)]) for q in qlst]]
return sum([sch(qlst+[q], x|q, (y|q)>>1, (z|q)<<1, qs) for q in qs if q & (x|y|z) == 0], [])
return sch([], 0, 0, 0, [1<<i for i in range(n)])
Leetcdoe 52题也是N皇后问题,与51不同之处是该题只需输出解的个数,我们可以用5行代码解决这个问题,这5行代码中有2行是Leetcode的接口,求解这个问题只需3行代码。
# Leetcode 52
class Solution:
def totalNQueens(self, n: int) -> int:
def s(l: List[int], x: int, y: int, z: int, qs: List[int]) -> List[List[str]]:
return 1 if len(l)==n else sum([s(l+[q], x|q, (y|q)>>1, (z|q)<<1, qs) for q in qs if q&(x|y|z)==0])
return s([], 0, 0, 0, [1<<i for i in range(n)])
对称性优化
N皇后问题的解中存在这很多对称性,其中一个最容易观察到的是左右的镜像对称,通过使用这个对称的特性可以进一步优化算法,将搜索空间缩小接近一半(对于偶数的N是刚好一半)。
怎样在我们已有的程序中使用对称性优化呢?这似乎并不显然,其实这个优化非常容易加进去。我们只要在第一行(或列)只搜索一半即可。
# Leetcode 52
class Solution:
def totalNQueens(self, n: int) -> int:
def s(l:int, x: int, y: int, z: int, qs: List[int]) -> List[List[str]]:
return 1 if l==n else sum([s(l+1, x|q, (y|q)>>1, (z|q)<<1, qs) for q in qs if q&(x|y|z)==0])
t = 2 * sum([s(1, 1<<i, (1<<i)>>1, (1<<i)<<1, [1<<j for j in range(n)]) for i in range(n//2)])
return t + (s(1, 1<<(n//2), (1<<(n//2))>>1, (1<<(n//2))<<1, [1<<j for j in range(n)]) if n&1 else 0)
欢迎各位朋友有空常来B站直播间一起交流学习!如果您喜欢我创造的内容,请关注、点赞、转发支持一下,助力纸质版《算法之法》早日面市!!