前两天上离散课,留了个有意思的作业:求解n皇后问题,并且语言不限制。由于c++学艺不精,编写复杂,java更适合面向对象,matlab已被制裁(平替北大天元没接触过),所以使用了更为简单的python来进行编写。
问题描述:
N 皇后问题源自国际象棋,所有棋子中权力最大的称为皇后,它可以直着走、横着走、斜着走(沿 45 度角),可以攻击移动途中遇到的任何棋子。N 皇后问题的具体内容是:如何将 N 个皇后摆放在 N*N 的棋盘中,使它们无法相互攻击。
为了更好理解,这里放一张从别处偷过来的图
问题分析:
n皇后问题的关键可以是在一行中或者在一列中或者同一个对角线上,有且只有一个皇后。
问题建模:
表示皇后的位置的方法
可以用一个列表来表示皇后位置mark[i] = j , 表示第i行第j列是皇后
def make(mark):
#标记皇后位置,比如mark[0] = 2,就是第一行第三列是皇后
r = [['N' for _ in range(len(mark))] for _ in range(len(mark))]
#Q表示皇后
for i in mark:
r[i][mark[i]] = 'Q'
#枚举
for k, v in enumerate(r):
r[k] = ''.join(v)
return r
判断是否可以放置皇后
首先我们引入个变量。对于皇后在棋盘上的放置方法,我们可以把在第i 行第 j 列上有皇后时为真,否则为假。
如果出现或者情况时,则表示和在一条对角线上。
mark[j] == i or abs(i-mark[j]) == cur - j
进行深度遍历
深度遍历大伙都会吧,不会的回去嗯补《数据结构》
for i in range(len(mark)):
#遍历每一列,如果可以就进行下一行的排序
mark[cur], down = i, True
for j in range(cur):
#如果冲突就不放皇后
if mark[j] == i or abs(i-mark[j]) == cur - j:
down = False
break
#递归
if down == True :
N_Queens(mark, cur+1 , ret)
代码包装
def make(mark):
#标记皇后位置,比如mark[0] = 2,就是第一行第三列是皇后
r = [['N' for _ in range(len(mark))] for _ in range(len(mark))]
#Q表示皇后
for i in mark:
r[i][mark[i]] = 'Q'
#枚举
for k, v in enumerate(r):
r[k] = ''.join(v)
return r
def N_Queens(mark, cur, ret):
#算法类似数据结构里面的深度优先遍历
if cur == len(mark):
#如果最后一行,记录并输出
ret.append(make(mark))
return
for i in range(len(mark)):
#遍历每一列,如果可以就进行下一行的排序
mark[cur], down = i, True
for j in range(cur):
#如果冲突就不放皇后
if mark[j] == i or abs(i-mark[j]) == cur - j:
down = False
break
if down == True :
N_Queens(mark, cur+1 , ret)
def solveNQueens(n):
ret = []
N_Queens([None] * n, 0, ret)
return ret
if __name__ == '__main__':
n = int(input("想要多少皇后的问题?"))
result = solveNQueens(n)
print('*' * n)
for r in result:
for f in r:
print(f)
print("*" * n)
后序
由于时间匆忙(上选修课的时候写的)后期也没有继续研究,最近看了看其他的算法,除了深度优先遍历以外,我也在想能不能用其他的算法来解决这个问题,比如:暴力搜索,把所有的情况都找出来然后一一对比是否符合,只是其时间复杂度很高(极高)并且在n极大时耗费时间量极大。(同学用cpp写的时间在n=13时用时2.6s)所以也没有兴趣继续往下探索了。