51.N皇后

目标

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

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

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

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

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

说明:

  • 1 <= n <= 9

思路

将 n 个皇后放在 n x n 的棋盘上,使它们不在同一行、不在同一列并且不在同一斜线上。返回所有不同的解决方案,使用 Q 表示皇后,. 表示空位。

枚举棋盘的每个格子,当该格子放置皇后时,其所在行、列、斜线的格子都不能有皇后。使用回溯算法,如果格子遍历完皇后数量刚好为 n 个则计数。

假设 (i, j) 位置上放置了皇后,那么所有 (i, *) (*, j) 以及所有满足 a - b == i - j || i + j == a + b(a, b) 都不能再放置皇后。

使用整数的二进制位来表示列是否允许放置皇后,从左到右对应二进制的低位到高位:

  • 由于逻辑与、或没有逆运算,所以必须以参数的方式传递,否则无法恢复现场
  • 如果我们在第 0 行的第 i 列放置了皇后,那么第 1 行的第 i - 1, i, i + 1 列无法放置皇后,使用位运算的角度来看就是对 1 << i, 1 << (i + 1), 1 << (i - 1) 相与,即 1 << i, (1 << i) << 1, (1 << i) >> 1。令 c = 1 << i,再考虑当前行与前面行的限制,下一行 不能放置皇后的列为:colIndexSet | c,(leftDiagonalSet | c) << 1,(rightDiagonalSet | c) >> 1),其中 colIndexSet 的 bit 1 表示无法放置皇后的列;leftDiagonalSet 的 bit 1 表示受 斜线限制,无法放置皇后的列;rightDiagonalSet 的 bit 1 表示受 斜线限制,无法放置皇后的列;c 表示将当前行放置皇后的列对应的 bit 位 置 1 所代表的数字
  • valid = ((1 << n) - 1) & ~(colIndexSet | leftDiagonalSet | rightDiagonalSet) 表示合法的位置
  • c = valid & -valid 得到 bit 1 的最低位表示的数字
  • Integer.bitCount(c - 1) 表示列下标
  • valid ^= c 将当前处理过的最低位 bit 1 置 0

当然也可以使用数组来记录不可放置皇后的列信息,差别不大,见 52_N皇后II

代码


/**
 * @date 2024-12-01 16:45
 */
public class SolveNQueens51 {

    public static class SolveNQueens {
        public List<List<String>> res;
        public int n;
        public int[] queens;

        public List<List<String>> solveNQueens(int n) {
            this.n = n;
            queens = new int[n];
            res = new ArrayList<>();
            backTracing(0, 0, 0, 0);
            return res;
        }

        public void backTracing(int row, int colIndexSet, int leftDiagonalSet, int rightDiagonalSet) {
            if (row == n) {
                List<String> solution = new ArrayList<>(n);
                char[] chars = new char[n];
                Arrays.fill(chars, '.');
                for (int i = 0; i < n; i++) {
                    chars[queens[i]] = 'Q';
                    solution.add(new String(chars));
                    chars[queens[i]] = '.';
                }
                res.add(solution);
                return;
            }
            int valid = ((1 << n) - 1) & ~(colIndexSet | leftDiagonalSet | rightDiagonalSet);
            while (valid > 0){
                int c = valid & -valid;
                queens[row] = Integer.bitCount(c - 1);
                backTracing(row + 1, colIndexSet | c,
                        (leftDiagonalSet | c) << 1,
                        (rightDiagonalSet | c) >> 1);
                valid ^= c;
            }
        }

    }

    public List<List<String>> res;
    public Set<Integer> rowIndexSet;
    public Set<Integer> colIndexSet;
    public Set<Integer> leftDiagonalSet;
    public Set<Integer> rightDiagonalSet;
    public ArrayList<int[]> indexList;
    public int end;
    public int n;

    public List<List<String>> solveNQueens(int n) {
        res = new ArrayList<>(n);
        rowIndexSet = new HashSet<>(n);
        colIndexSet = new HashSet<>(n);
        leftDiagonalSet = new HashSet<>(n);
        rightDiagonalSet = new HashSet<>(n);
        indexList = new ArrayList<>(n);
        end = n * n;
        this.n = n;
        for (int i = 0; i < n; i++) {
            backTracing(i);
        }
        return res;
    }

    public void backTracing(int index) {
        int row = index / n;
        int col = index % n;
        int leftDiagonal = row - col;
        int rightDiagonal = row + col;
        rowIndexSet.add(row);
        colIndexSet.add(col);
        leftDiagonalSet.add(leftDiagonal);
        rightDiagonalSet.add(rightDiagonal);
        indexList.add(new int[]{row, col});
        if (indexList.size() == n) {
            StringBuilder sb = new StringBuilder();
            List<String> solution = new ArrayList<>(n);
            for (int[] cor : indexList) {
                for (int i = 0; i < n; i++) {
                    if (i == cor[1]) {
                        sb.append('Q');
                    } else {
                        sb.append('.');
                    }
                }
                solution.add(sb.toString());
                sb.setLength(0);
            }
            res.add(solution);
        }
        for (int i = index + 1; i < end; i++) {
            int r = i / n;
            int c = i % n;
            int ld = r - c;
            int rd = r + c;
            if (rowIndexSet.contains(r) || colIndexSet.contains(c)
                    || leftDiagonalSet.contains(ld)
                    || rightDiagonalSet.contains(rd)) {
                continue;
            }
            backTracing(i);
        }
        rowIndexSet.remove(row);
        colIndexSet.remove(col);
        leftDiagonalSet.remove(leftDiagonal);
        rightDiagonalSet.remove(rightDiagonal);
        indexList.remove(indexList.size() - 1);
    }

}

性能

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注