The classic N-Queens problem finds a way to place n queens on an n×n chessboard such that no two queens attack each other. This is my solution to the N-Queens problem.
class Solution(object):
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
grid = [['.' for _ in range(n)] for _ in range(n)]
solved = self.helper(n, 0, grid)
if solved:
return ["".join(item) for item in grid]
else:
return None
def helper(self, n, row, grid):
if n == row:
return True
for col in range(n):
if self.is_safe(row, col, grid):
grid[row][col] = 'Q'
if self.helper(n, row + 1, grid):
return True
else:
grid[row][col] = '.'
return False
def is_safe(self, row, col, board):
for i in range(len(board)):
if board[row][i] == 'Q' or board[i][col] == 'Q':
return False
i = 0
while row - i >= 0 and col - i >= 0:
if board[row - i][col - i] == 'Q':
return False
i += 1
i = 0
while row + i < len(board) and col + i < len(board):
if board[row + i][col - i] == 'Q':
return False
i += 1
i = 1
while row + i < len(board) and col - i >= 0:
if board[row + i][col - i] == 'Q':
return False
i += 1
i = 1
while row - i >= 0 and col + i < len(board):
if board[row - i][col + i] == 'Q':
return False
i += 1
return True
if __name__ == '__main__':
solution = Solution()
print(solution.solveNQueens(8))
An extension to this problems states, find all possible solutions and return them in the form of a list. I tried to extend my solution by adding a column variable to the helper method, which starts at 0 to keep track of one complete solution and the start of the next. Thus the base case becomes
if row == n and col == n:
return True
But that approach doesn't work as every successive recursive call ends up in the wrong column. Can someone help extend this solution to find all possible solutions.
Great question! N-queens is also a great recursive problem :) You are actually really close to getting what you want and you don't have to modify your code too much.
A good way to think about it is to understand the two different problems you are looking at. Your current approach is using backtracking to find the first solution possible. What you want to do is find all solutions, a similar problem that just requires you to think about your base case differently.
In your current setup, if your base case returns True, the parent call will short circuit and return True as well. This is ideal when trying to find just any single solution because once we found one that works, we know we can stop looking. However, as a result, we don't keep exploring other paths.
A way to think about backtracking is that you are basically creating a tree of possible boards resulting from possible moves. To find the first solution, as soon as you get to a leaf node, or a winning state, you return all the way back up. However, what you want to do is instead keep traversing all the other paths of the tree and keep looking for leaf winning states and recording them along the way.
So, a simple way to modify your current approach is to modify your base case so that instead of returning True, it records the state of the board, a winning state, in a variable tracking all solutions. Additionally, now in your recursive case, when you make the recursive call you don't check for if it returns True or False, but rather just keep chugging along in the for loop and trying all moves.
I modified your solution like this and got there are 92 solutions, something that the internet confirms is true :)
class Solution(object):
def __init__(self):
self.solutions = []
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
grid = [['.' for _ in range(n)] for _ in range(n)]
solved = self.helper(n, 0, grid)
print len(self.solutions)
if solved:
return ["".join(item) for item in grid]
else:
return None
def helper(self, n, row, grid):
if n == row:
print "wooo"
self.solutions.append(copy.deepcopy(grid))
return
for col in range(n):
if self.is_safe(row, col, grid):
grid[row][col] = 'Q'
self.helper(n, row + 1, grid)
grid[row][col] = '.'
I hope this helps!