N皇后问题

第一次尝试:
# 皇后不能在同一行,同一列,和对角线上
# 实现不在同一行,即行剪枝,进行逐行选择元素
# 实现不在同一列,即列剪枝,创建一个列表,用于记录每一列是否有皇后
# 实现不在对角线上,即对角线剪枝,对皇后坐标[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)))

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值