研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
即皇后所放置的位置需要满足以下三个条件:
·同一横线上不能存在皇后
·同一竖线上不能存在皇后
·以该皇后位置为中心,斜率为正负一的两条斜线上不能存在皇后
其中前两个条件我相信大多数人都能够轻易实现,主要是第三个斜线问题比较难以解决。在这里我是使用的比较简单粗暴的办法来实现它,所以看起来可能有些愚笨。
解题过程首先设置一个二维列表充当棋盘,其中用‘.’来表示该位置为空,用‘Q’来表示该位置已被放置皇后。
n为棋盘的大小
n = 5 chess = [['.' for i in range(n)] for i in range(n)]
然后是简单的检查该列上有没有皇后已经存在,如果已经存在则返回False,如果检查完毕仍为发现皇后存在则返回True 。
def checkcol(col):
for row in range(n):
if chess[row][col] == 'Q':
return False
return True
然后是检查斜线上是否存在皇后,这里我采取的方法是将两条斜线分割成四份,然后逐个检查。其中i,j为当前想要放置皇后的位置。
def checkslant(i,j):
row = i-1
col = j+1
while row >= 0 and col < n:
if chess[row][col] == 'Q':
return False
else:
row -= 1
col += 1
row = i + 1
col = j + 1
while row < n and col < n:
if chess[row][col] == 'Q':
return False
else:
row += 1
col += 1
row = i - 1
col = j - 1
while row >= 0 and col >=0:
if chess[row][col] == 'Q':
return False
else:
row -= 1
col -= 1
row = i + 1
col = j - 1
while row < n and col >= 0:
if chess[row][col] == 'Q':
return False
else:
row += 1
col -= 1
return True
到这里,解决n皇后问题的前置要求已经写完。至于为什么不需要检查该行上有没有存在皇后,是因为我们在回溯的过程中只需要在每行上放置一个皇后随即就进入到下一行,所以无需检查。
def backtrace(row):
if row == n:
tmp = [''.join(x) for x in chess]
tmp_res.append(tmp)
return
for col in range(n):
if checkcol(col) and checkslant(row,col):
chess[row][col] = 'Q'
backtrace(row+1)
chess[row][col] = '.'
该函数就是n皇后问题的核心函数,主要用到的是算法中的回溯思想。在做算法题到现在,自我感觉回溯算法是算法中最为简单的一种算法,只需要解决两个问题,即何时退出和下一个新区域在哪。
首先我们先来考虑每次开辟的新区域在哪:
在前面解释为什么不需要检查行上面有无皇后时说了一下,由于皇后放置的规则上只允许一行上放置一个皇后,所以咱们在一行上正确放置一个皇后后就无需再向后继续进行,直接去下一行找可以正确放置皇后的位置即可。所以咱们每次开辟的新区域就是棋盘的下一行。
其次,在来考虑何时退出的问题:
每次在进入新区域的前提是咱们已经把旧区域的问题解决好了,也就是咱们在进入第i行时意味着咱们已经在0—i-1行正确放置了皇后,由于棋盘是NxN的二维棋盘,所以当咱们进入第n(下表从0开始)行时,意味着前n-1行已经正确放置了皇后。所以结束条件为当前行为n。
每次开辟新区域后,咱们要在当前行上寻找一个可以放置皇后的位置,所以依次遍历该行上的位置。当该位置的竖线和斜线上没有皇后存在时,将皇后放置于该位置,即赋值为‘Q’。随后进入下一行,在进行下一循环时要撤掉皇后,即将该位置的值再赋为‘.’。



