leetcode习题集——52. N皇后 II

129 篇文章 0 订阅
42 篇文章 0 订阅

题目

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

在这里插入图片描述

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

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

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

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

算法1

//回溯递归方法解决
public class P52_NQueensII {
    int count = 0;
    public int totalNQueens(int n) {
        helper(new ArrayList<String>(), 0, new boolean[n], new boolean[2*n], new boolean[2*n], n);
        return count;
    }

    private void helper(List<String> board, int row, boolean[] cols, boolean[] d1, boolean[] d2, int n){
        if (row == n) {
            count++;
            return;
        }
        for (int col=0; col<n; col++){
            int id1 = col - row + n;
            int id2 = col + row;
            if (!cols[col] && !d1[id1] && !d2[id2]){
                char[] r = new char[n];
                Arrays.fill(r, '.');
                r[col] = 'Q';
                board.add(new String(r));
                cols[col] = true;
                d1[id1] = true;
                d2[id2] = true;
                helper(board, row+1, cols, d1, d2, n);
//                还原过程
                board.remove(board.size()-1);
                cols[col] = false;
                d1[id1] = false;
                d2[id2] = false;
            }
        }
    }
}

思路:基本的回溯思想,详见leetcode习题集——51. N皇后

算法2(位运算法,目前最高效)

//深度优先方法解决
//位运算解决
public class P52_NQueensII2 {
    private int count = 0;
    public int totalNQueens(int n) {
        if(n==1) return 1;
        DFS(0, 0, 0, 0, n);
        return count;
    }

    public void  DFS(int row, int col, int pie, int na, int n) {
        if (row >= n) {
            count++;
            return;
        }
        int a = (~(col | pie | na));
        int b = ((1 << n) - 1);
        int bits = a & b;
        while (bits > 0) {
            int P = bits & (-bits);
            DFS(row + 1, col | P, (P | pie) << 1, (na | P) >> 1, n);
            bits = bits & (bits - 1);
        }
    }
}

代码解析:

  1. row代表当前处理的行号,本算法根据行号进行遍历,保证一行放置一个皇后,不产生冲突
  2. colpiena分别代表了列、左斜对角线、右写对角线上的位置禁用情况
  3. bit代表为计算之后当前行的禁用情况,1代表可以放置,0代表禁用行
  4. int P = bits & (-bits);该等式等价于int P = bits & (~bits+1),意为取bit上为1的最低位
  5. DFS(row + 1, col | P, (P | pie) << 1, (na | P) >> 1, n)
    5.1 row+1代表处理下一行
    5.2 col | P表示将当前列设为禁用
    5.3 (P | pie) << 1 表示将当前位置的左边一位设为禁用(即左下,左对角线)
    5.4 (na | P) >> 1表示将当前位置的右边以为设为禁用(即左下,左对角线)
  6. bits = bits & (bits - 1),表示将最低位的1置为0,表示该位已经遍历过了

以下以n=4为例观察一下算法运行的步骤

  1. 初始情况计算bits

在这里插入图片描述

bits=a&b=1111(2进制)

  1. 取最低位为1的P,放置皇后

在这里插入图片描述
在这里插入图片描述

P=bits&(~bits+1)=0001(2进制)

  1. 进入下一层递归(下一行),同时修正禁用位置

在这里插入图片描述

col=0001(2进制)

pie=0010(2进制)

na=0000(2进制)

a=~(0001|0010|0000)=1100

bits=a&b=1100&1111=1100

可以看出,上一行中的同列和斜对角都被禁用了
  1. 取最低位为1的P,放置皇后

在这里插入图片描述
在这里插入图片描述

  1. 进入下一层递归(下一行),同时修正禁用位置

在这里插入图片描述

col=0001|0100=0101(2进制)

pie=(0010|0100)<<1=1100(2进制)

na=(0000|0100)>>1=0010(2进制)

a=~(0101|1100|0010)=0000

bits=a&b=0000&1111=0000

由此可知,本行所有位置都被禁用掉了, bits=0,所以本层递归结束

  1. 回到上一行的bits = bits & (bits - 1)

在这里插入图片描述
执行bits = bits & (bits - 1)前bits值为1100
在这里插入图片描述

bits=bits&(bits-1)=1100&1011=1000

此步骤的意义是将最低位的1置为0,即标记皇后已经在这里放置过,如果bits>0,则需要继续向高位遍历。
  1. 继续向高位遍历

在这里插入图片描述

继续向高位遍历的后续重复步骤3~6即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值