经典N皇后问题

N皇后问题

n 皇后问题:
如何将 n 个皇后放置在 n×n 的棋盘上,
并且使皇后彼此之间不能相互攻击(不在同一行,同一列,同一对角线上)。
给定一个整数 n ,返回有多少种摆放方法。

两种解法

/**
 * @author IT00ZYQ
 * @Date 2021/4/8 21:50
 **/
public class N皇后 {

    /**
     * 暴力递归法
     * @param n
     * @return
     */
    public static int way1(int n) {
        if (n < 0) {
            return -1;
        }
        int[] record = new int[n];
        return wayFun1(0, n, record);
    }

    /**
     * @param cur 当前需要的摆放的是第cur个皇后
     * @param n 总共有n-1个皇后需要摆放
     * @param record 前cur-1个皇后摆放的列
     * @return
     */
    private static int wayFun1(int cur, int n, int[] record) {
        if (cur == n) {
            return 1;
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (isValid(record, cur, i)) {
                record[cur] = i;
                res += wayFun1(cur + 1, n, record);
                // 由于每次进行操作都会修改record[cur]的值,此处不需要还原现场
            }
        }
        return res;
    }

    private static boolean isValid(int[] record, int i, int j) {
        for (int k = 0; k < i; k++) {
            // 用于判断(k, record[k]) 与 (i, j)是否在同一列或者同一对角线上
            if (record[k] == j || Math.abs(i - k) == Math.abs(j - record[k])) {
                return false;
            }
        }
        return true;
    }


    /**
     * 通过位运算加快常数级运算
     * @param n n皇后问题
     * @return 摆放方案
     */
    public static int way2(int n) {
        // 位运算方法最高只支持32皇后问题
        if (n < 0 || n > 32) {
            return -1;
        }
        // (1<<n)-1 -> 如果n是8皇后,则其二进制表示为00...00 1111 1111
        // 即最后8位为1,其他位都为0
        int limit = n == 32 ? -1 : (1 << n) - 1;
        return wayFun2(limit, 0, 0, 0);
    }


    public static int wayFun2(int limit, int colLimit, int leftLimit, int rightLimit) {
        // 当列限制已满,说明已摆放了n皇后,返回1
        if (colLimit == limit) {
            return 1;
        }

        int res = 0;
        // t表示在colLimit、leftLimit、rightLimit限制下,还有多少种可尝试的方案
        // 可尝试的位置为1,被限制的位置为0
        int t = limit & ~(colLimit | leftLimit | rightLimit);
        while (t != 0) {
            // 获取t中最右边的1
            int p = t & (~t + 1);
            res += wayFun2(limit,
                    colLimit | p,
                    (leftLimit | p) << 1,
                    (rightLimit | p) >>> 1);
            t -= p;
        }
        return res;
    }

    public static void main(String[] args) {
       for (int i = 0; i <= 12; i++) {
            System.out.printf("当前为%d皇后问题,暴力递归法答案:%d,位运算改进法答案:%d\n", i, way1(i), way2(i));
        }
    }
}

问题答案

当前为0皇后问题,暴力递归法答案:1,位运算改进法答案:1
当前为1皇后问题,暴力递归法答案:1,位运算改进法答案:1
当前为2皇后问题,暴力递归法答案:0,位运算改进法答案:0
当前为3皇后问题,暴力递归法答案:0,位运算改进法答案:0
当前为4皇后问题,暴力递归法答案:2,位运算改进法答案:2
当前为5皇后问题,暴力递归法答案:10,位运算改进法答案:10
当前为6皇后问题,暴力递归法答案:4,位运算改进法答案:4
当前为7皇后问题,暴力递归法答案:40,位运算改进法答案:40
当前为8皇后问题,暴力递归法答案:92,位运算改进法答案:92
当前为9皇后问题,暴力递归法答案:352,位运算改进法答案:352
当前为10皇后问题,暴力递归法答案:724,位运算改进法答案:724
当前为11皇后问题,暴力递归法答案:2680,位运算改进法答案:2680
当前为12皇后问题,暴力递归法答案:14200,位运算改进法答案:14200