Leetcode-51. N皇后

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回 n 皇后不同的解决方案的数量。

示例:

输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

本题使用位运算进行速度最快的题解,很难理解,但是速度飞快,先上一下用set题解的代码

class Solution {
   
    public int count = 0;
    public int totalNQueens(int n) {
   
        Set<Integer> left = new HashSet<>();
        Set<Integer> right = new HashSet<>();
        Set<Integer> cols = new HashSet<>();
        List<Integer> tmp = new ArrayList<>();
        this.dfs(0, n, cols, left, right, tmp);
        return this.count;
    }

    public void dfs(int level, int n, Set<Integer> cols, Set<Integer> left, Set<Integer> right, List<Integer> tmp){
   
        if (tmp.size()==n) {
   
            // res.add(tmp); DO NOT ADD TMP DIRECTLY! tmp will change in backtracing! add a copy of tmp!
            this.count++;
            return;
        }
        for (int i=0;i<n;i++) {
   
            if (!cols.contains(i) && !left.contains(level+i) && !right.contains(level-i)) {
   
                cols.add(i);
                left.add(level+i);
                right.add(level-i);
                tmp.add(i);
                this.dfs(level+1, n, cols, left, right, tmp);
                cols.remove(i);
                left.remove(level+i);
                right.remove(level-i);
                tmp.remove(Integer.valueOf(i)); // remove(i) will not remove the i value in tmp ,but will remove the ith value! MARK THAT!
            }
        }
        return ;
    }
}

上一张伪代码的图

首先解释下 x & (-x),这个的作用是取得最后一位的1的位置,
例如20为10100,-20是补码,也就是取反加一,01011 + 00001 = 01100,10100 & 01100 = 00100,作用确实是取得最后一位1的位置

  • Java
class Solution {
   
    public int count;
    public int N;
    public int totalNQueens(int n) {
   
        this.N = n;
        this.dfs(0,0,0,0); // 设0为皇后可以放置的位置,则开始的时候4个位置都能放,0000还是0,所以传入0
        return this.count;
    }
    public void dfs(int level, int col, int pie, int na) {
   
        if (level==this.N) {
   
            this.count++;
            return ;
        }
        int zeroIsGood = ~(col|pie|na);// 列,撇,捺进行或操作,所有为1的位置都会有皇后攻击,因此0的位置是可放皇后的位置,取反变成1为可用的位置便于计算,但是由于取反了,前面有32-n数量的1,要把这32-n数量的1都变成0,需要与操作后n位都是1的数
        int makeN_1 = (int)Math.pow(2,this.N) - 1;
        int bits = zeroIsGood & makeN_1; // 得到包含可用位置信息的数
        while (bits!=0) {
   // 还有能用的位置的时候,要将所有位置的1都进行计算
            int p = bits & (-bits); // 取得包含最后一个1位置的数
            this.dfs(level+1, col|p, (pie|p)<<1, (na|p)>>1); // 下一行中,列要包含这次被占用的位置,撇要包含这个位置向左移动一格的位置,捺要包含这个位置向右移动一格的位置,出界的位置无论是左边还是右边都是用0补上的,而0代表可用,所以莫得问题
            bits = bits & (bits-1); // 消除最后已经被计算过的1,继续计算下一个1
        }
    }
}
  • Python
class Solution:
    def totalNQueens(self, n: int) -> int:
        self.N = n
        self.count = 0
        self.dfs(0,0,0,0)
        return self.count
    
    def dfs(self,level, col, pie, na):
        if level==self.N: 
            self.count += 1
            return
        bits = (~(col|pie|na)) & (2**self.N - 1)
        while bits:
            p = bits & (-bits)
            self.dfs(level+1, col|p, (pie|p)<<1, (na|p)>>1)
            bits = bits & (bits-1)