一.问题描述
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q'
and '.'
both
indicate a queen and an empty space respectively.
For example,
There exist two distinct solutions to the 4-queens puzzle:
[ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ]注意理解题意,n皇后问题的限制是:每一行每一列上均只能有且只有一个皇后。
二.代码编写
典型的回溯问题,按行回溯或按列都可以。我的回溯方式是酱的,对每一列,从上到下选第一个遇到的可以填入皇后的位置填入,然后进入下一列,如果找不到可以填的位置就回溯到前一行。
一个问题是如何判断当前位置是否可以填入,方法很简单,建3个list,分别对应行,左斜对角线,右斜对角线相应位置是否可用。
因此相应位置与左对角线的对应关系是,(i , j)对应第 i+j 条左对角线。
(i , j )对应第 n - i + j 条右对角线。
backtracking还有一个要注意的点就是回溯的停止条件,这个应该在之前用到backtracking的时候就讲到过,这里再强调一下,就是我这里的遍历方向是酱的:
所以停止条件是一直回退到了第5行,第1列。所以当行数没越界或者列数没越界时,都要继续。这就有一个问题需要再循环中处理了,就是行数越界的时候也需要backtracking。
具体代码如下:【这次代码注释hin全哦,2333】
'''
@ author: wttttt at 2016.12.04
@ problem description see: https://leetcode.com/problems/n-queens/
@ solution explanation see: http://blog.youkuaiyun.com/u014265088/article
@ github:https://github.com/wttttt-wang/leetcode
@ backtracking
'''
import copy
class Solution(object):
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
# marigin condition
if n == 0:
return None
# backtracking
rlist = [] # all solutions, list of lists, attention to use copy.deepcopy()
rstring = ''
for i in range(n):
rstring += '.'
rlist_one = [rstring for i in range(n)] # initialize for one solution
row_ind = [0 for i in range(2*n-1)] # indicates if the corresponding row is used
left_ind = [0 for i in range(2*n-1)] # indicates if the left diagonal is used
right_ind = [0 for i in range(2*n-1)] # indicates if the right diagonal is used
i = 0 # indicates the row number
j = 0 # indicates the col number
while i < n or j > 0: # the stop conditon of backtracking
while i >= n: # no available solution for this col, then backtracking
j -= 1
if j < 0: # no more possible solutions
return rlist
# find the Queen in previous col
for row_i in range(n):
if rlist_one[row_i][j] == 'Q':
break
row_ind[row_i] = 0
left_ind[row_i+j] = 0
right_ind[n-row_i+j-1] = 0
rlist_one[row_i] = rlist_one[row_i][:j] + '.' + rlist_one[row_i][j+1:]
i = row_i + 1
# for the col j, find the likely solution with the upperest row
flag_found = 0
for nn in range(i, n):
if row_ind[nn] == 1 or left_ind[nn+j] == 1 or right_ind[n-nn+j-1] == 1:
# this row is already used or there has been a Queen in the left/right diagonal
continue
else:
flag_found = 1 # indicates that a targeted position is found
break
if flag_found == 1: # u have found a feasible position in col j
rlist_one[nn] = rlist_one[nn][:j] + 'Q' + rlist_one[nn][j+1:]
if j == n - 1:
# append this solution to rlist
rlist.append(copy.deepcopy(rlist_one))
rlist_one[nn] = rlist_one[nn][:j] + '.' + rlist_one[nn][j + 1:]
i = nn + 1 # continue finding the next solution
else:
i = 0 # iterates from the first row
row_ind[nn] = 1 # now there is a Queen is this row
left_ind[nn+j] = 1
right_ind[n-nn+j-1] = 1
j += 1 # iterates to next col
else: # no feasible solution for this col, then backtracking
j -= 1
# find the Queen in previous col
for row_i in range(n):
if rlist_one[row_i][j] == 'Q':
break
row_ind[row_i] = 0
left_ind[row_i+j] = 0
right_ind[n-row_i+j-1] = 0
rlist_one[row_i] = rlist_one[row_i][:j] + '.' + rlist_one[row_i][j+1:]
i = row_i + 1
return rlist
so = Solution()
n = 5
print so.solveNQueens(n)