第一次尝试:
# 皇后不能在同一行,同一列,和对角线上
# 实现不在同一行,即行剪枝,进行逐行选择元素
# 实现不在同一列,即列剪枝,创建一个列表,用于记录每一列是否有皇后
# 实现不在对角线上,即对角线剪枝,对皇后坐标[i,j]也可表示为[row,col],row与col表示索引
# 1.对于一条向左倾斜对角线上的每个点,都有i - j = n,n为常数,这条对角线成为主对角线
# 2.对于一条向右倾斜对角线上的每个点,都有i + j = n,n为常数,这条对角线成为副对角线
def n_queens(n: int):
# 描述问题状态:#为空,Q为皇后
state = [['#'] * n] * n
# 记录每一列是否有皇后
cols = [False] * n
# 记录对角线是否有皇后,创建列表,确定对角线列表的长度
# 0 <= row <=n - 1,0 <= col <= n - 1,因为row和col表示索引,所以为n - 1
# 则0 <= row + col <= 2n - 2
# 则1 - n <= row - col <= n - 1
# 所以长度为2n - 2 + 1 = 2n - 1,为什么加一,因为还包括0这一个位置
diags1 = [False] * (2 * n - 1)
diags2 = [False] * (2 * n - 1)
# 记录皇后数量
count = 0
# 将符合条件的解存储
res = []
# 记录行
row = 0
# 进行回溯
backtrack(row, n, state, cols, diags1, diags2, count, res)
return res
def backtrack(row, n, state, cols, diags1, diags2, count, res):
# 判断皇后是否放置完毕
if count == n:
# 若为解,即皇后放置完毕,存储
res.append(list(state))
# 存储解后结束本轮选择
return
# 若不是解,遍历所有列
for col in range(n):
# 计算主,副对角线
diag1 = row - col + n - 1
diag2 = row + col
# 对选择进行剪枝
if not cols[col] and not diags1[diag1] and not diags2[diag2]:
# 尝试,更新状态
state[row][col] = 'Q'
count += 1
cols[col] = True
diags1[diag1] = True
diags2[diag2] = True
# 回溯,判断尝试是否后是否为解,及是否进行下一轮放置
backtrack(row + 1, n, state, cols, diags1, diags2, count, res)
# 回退,撤回当前选择,恢复状态
state[row][col] = '#'
cols[col] = False
diags1[diag1] = False
diags2[diag2] = False
if __name__ == '__main__':
n = 4
print(n_queens(n))
print(len(n_queens(n)))
diag1 = row - col + n - 1主对角线为什么要加n - 1?
如4皇后问题其主对角线取值为
-3 -2 -1 0 1 2 3
主对角线列表长度为2 * n - 1 即为7
索引为
0 1 2 3 4 5 6
将主对角线每个值加上n - 1即为3得
0 1 2 3 4 5 6
那么将做到对角线取值与对角线列表的索引对应
如果直接加3将只适用于4皇后问题,所以使用n - 1
错误:
1.res结果列表添加解的方式错误,造成输出解全为井号
2.回退操作中为对皇后数量进行更新,造成解的数目错误
第二次尝试:运用列表推导式添加结果
# 皇后不能在同一行,同一列,和对角线上
# 实现不在同一行,即行剪枝,进行逐行选择元素
# 实现不在同一列,即列剪枝,创建一个列表,用于记录每一列是否有皇后
# 实现不在对角线上,即对角线剪枝,对皇后坐标[i,j]也可表示为[row,col],row与col表示索引
# 1.对于一条向左倾斜对角线上的每个点,都有i - j = n,n为常数,这条对角线成为主对角线
# 2.对于一条向右倾斜对角线上的每个点,都有i + j = n,n为常数,这条对角线成为副对角线
def n_queens(n: int):
# 描述问题状态:#为空,Q为皇后
state = [['#'for _ in range(n)] for _ in range(n)]
# 记录每一列是否有皇后
cols = [False] * n
# 记录对角线是否有皇后,创建列表,确定对角线列表的长度
# 0 <= row <=n - 1,0 <= col <= n - 1,因为row和col表示索引,所以为n - 1
# 则0 <= row + col <= 2n - 2
# 则1 - n <= row - col <= n - 1
# 所以长度为2n - 2 + 1 = 2n - 1,为什么加一,因为还包括0这一个位置
diags1 = [False] * (2 * n - 1)
diags2 = [False] * (2 * n - 1)
# 记录皇后数量
count = 0
# 将符合条件的解存储
res = []
# 记录行
row = 0
# 进行回溯
backtrack(row, n, state, cols, diags1, diags2, count, res)
return res
def backtrack(row, n, state, cols, diags1, diags2, count, res):
# 判断皇后是否放置完毕
if count == n:
# 若为解,即皇后放置完毕,存储
res.append([list(row) for row in state])
# 存储解后结束本轮选择
return
# 若不是解,遍历所有列
for col in range(n):
# 计算主,副对角线
diag1 = row - col + n - 1
diag2 = row + col
# 对选择进行剪枝
if not cols[col] and not diags1[diag1] and not diags2[diag2]:
# 尝试,更新状态
state[row][col] = 'Q'
count += 1
cols[col] = True
diags1[diag1] = True
diags2[diag2] = True
# 回溯,判断尝试是否后是否为解,及是否进行下一轮放置
backtrack(row + 1, n, state, cols, diags1, diags2, count, res)
# 回退,撤回当前选择,恢复状态
state[row][col] = '#'
count -= 1
cols[col] = False
diags1[diag1] = False
diags2[diag2] = False
if __name__ == '__main__':
n = 4
print(n_queens(n))
print(len(n_queens(n)))
精简代码:
状态更新中,重复更改布尔变量语句合为一句,不再记录皇后数量即删除count,以放置完第四行为结束条件
# 皇后不能在同一行,同一列,和对角线上
# 实现不在同一行,即行剪枝,进行逐行选择元素
# 实现不在同一列,即列剪枝,创建一个列表,用于记录每一列是否有皇后
# 实现不在对角线上,即对角线剪枝,对皇后坐标[i,j]也可表示为[row,col],row与col表示索引
# 1.对于一条向左倾斜对角线上的每个点,都有i - j = n,n为常数,这条对角线成为主对角线
# 2.对于一条向右倾斜对角线上的每个点,都有i + j = n,n为常数,这条对角线成为副对角线
def n_queens(n: int):
# 描述问题状态:#为空,Q为皇后
state = [['#'for _ in range(n)] for _ in range(n)]
# 记录每一列是否有皇后
cols = [False] * n
# 记录对角线是否有皇后,创建列表,确定对角线列表的长度
# 0 <= row <=n - 1,0 <= col <= n - 1,因为row和col表示索引,所以为n - 1
# 则0 <= row + col <= 2n - 2
# 则1 - n <= row - col <= n - 1
# 所以长度为2n - 2 + 1 = 2n - 1,为什么加一,因为还包括0这一个位置
diags1 = [False] * (2 * n - 1)
diags2 = [False] * (2 * n - 1)
# 将符合条件的解存储
res = []
# 记录行
row = 0
# 进行回溯
backtrack(row, n, state, cols, diags1, diags2, res)
return res
def backtrack(row, n, state, cols, diags1, diags2, res):
# 判断皇后是否放置完毕
if row == n:
# 若为解,即皇后放置完毕,存储
res.append([list(row) for row in state])
# 存储解后结束本轮选择
return
# 若不是解,遍历所有列
for col in range(n):
# 计算主,副对角线
diag1 = row - col + n - 1
diag2 = row + col
# 对选择进行剪枝
if not cols[col] and not diags1[diag1] and not diags2[diag2]:
# 尝试,更新状态
state[row][col] = 'Q'
cols[col] = diags1[diag1] = diags2[diag2] = True
# 回溯,判断尝试是否后是否为解,及是否进行下一轮放置
backtrack(row + 1, n, state, cols, diags1, diags2, res)
# 回退,撤回当前选择,恢复状态
state[row][col] = '#'
cols[col] = diags1[diag1] = diags2[diag2] = False
if __name__ == '__main__':
n = 4
print(n_queens(n))
print(len(n_queens(n)))