八皇后时间复杂度 回溯_N皇后问题

题目

在n*n格的棋盘上放置彼此不受攻击的n个皇后。由于皇后可以攻击与之处于同一行或同一列或在同一斜线上的棋子。n皇后问题等价于在n*n的棋盘上放置n个皇后,任何两个皇后不放在同一列或同一行或同一斜线上。

DFS + 剪枝法


解法分析:最粗暴的做法是每一行遍历列,然后每次和前面的所有元素判断一下是不是互相攻击。复杂度太高。DFS+剪枝的做法是,从上往下遍历层,每一层遍历列,由于每一撇上的元素col + row都是相等的,每一捺上的元素 col - row都是相等的,所以对于每一个当前可行的元素,把它的col、col + row、col - row存储到各自的集合里面,新的元素如果这三个数都不在对应集合里面,那么是可行的。遍历到第n层(最后一层是n-1层)直接把当前的状态放到result里面。

def n_queens(n):
    if n < 1:
        return []
    result = []
    cols, pie, na = set(), set(), set()

    def dfs(n, row, cur_state):
        if row >= n:
            result.append(cur_state)
            return
        for col in range(n):
            if col in cols or row + col in pie or row - col in na:
                continue  # 不合适,跳过

            # 更新flag
            cols.add(col)
            pie.add(row + col)
            na.add(row - col)

            dfs(n, row+1, cur_state+[col])
            # 恢复现场
            cols.remove(col)
            pie.remove(row + col)
            na.remove(row - col)
    def _generate_result(n):
        board = []
        for res in result:
            for i in res:
                board.append("."*i + "Q" + "." * (n-i-1))
        return [board[i:i+n] for i in range(0, len(board), n)]

    dfs(n, 0, [])
    return _generate_result(n)
print(n_queens(4))

简洁版。因为flag是dfs()函数的参数,因此不需要恢复现场。最后打印的时候采用二维的列表推导式,简化代码。

def n_queens(n):
    if n < 1:
        return []
    result = []
    def dfs(cols, xy_dif, xy_sum):
        row = len(cols)
        if row == n:
            result.append(cols)
            return None
        for col in range(n):
            if col not in cols and row - col not in xy_dif and row + col not in xy_sum:
                dfs(cols + [col], xy_dif + [row - col], xy_sum + [row + col])
    dfs([], [], [])
    return [["."*i + "Q" + "."*(n-i-1) for i in res] for res in result]
print(n_queens(4))

回溯法

回溯算法的解释如下:

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

回溯法可以看作一种DFS。对于某一个搜索树来说(搜索树是起记录路径和状态判断的作用),回溯和DFS,其主要的区别是,回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树。

回溯法和DFS很相似,这里不再展开了。

位运算解法(最高效)

这是n皇后问题的最高效解法。

用cols、pie、na是三个数字,其二进制表示之前的列、撇和捺上的皇后,0表示没有,1表示有。

bits =(~(cols | pie | na))&((1<< n)-1)是用来得到当前的空位的,前面三个数相或之后得到的0位置是可以放皇后的,取反之后的1位置是可以放皇后的。由于一个数有很多位,我们只需要后n位,所以与上后面的((1<< n)-1)。

bits &-bits可以得到最低位的1。

调用下一层dfs的时候,row直接加1,col或当前的列即可,pie或上当前列之后要左移一位,na或上当前列后要右移一位。

bits &(bits -1)作用是去掉最低位的1。

def n_queens(n):
    if n < 1:
        return []
    count = [0]
    def dfs(n, row, cols, pie, na):
        if row >= n:
            count[0] = count[0] + 1
            return
        bits = (~(cols | pie | na)) & ((1 << n) - 1)
        while bits:
            p = bits & -bits 
            dfs(n, row+1, cols | p, (pie | p) << 1, (na | p) >> 1)
            bits = bits & (bits - 1) 

    dfs(n, 0, 0, 0 ,0)
    return count[0]

print(n_queens(4))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值