【八皇后】给定一个大小为 n 的正方形国际象棋棋盘,求有多少种方式可以放置 n 个皇后并使得她们互不攻击,即每一行、列、左斜、右斜最多只有一个皇后。

给定一个大小为 n 的正方形国际象棋棋盘,求有多少种方式可以放置 n 个皇后并使得她们互不攻击,即每一行、列、左斜、右斜最多只有一个皇后。
输入是一个整数 n,输出是一个整数 m,表示所有的棋盘表示方法。

/**
 * 假设第一行皇后摆放在坐标(0, 1)
     0       1(Q)    2       3
             |
             |
     0 —— —— 1 —— —— 2       3
    现在需要摆放第二行,第一行与第二行的行差值为一,这里是个等腰三角形
    所以左斜线位置为(x, 1 - 1 = 0),右斜线位置为(x, 1 + 1 = 2),同理可计算出其他行之间的关系
     0       1       2       3

     0       1       2       3
 */
function func(n) {
    // 记录有几种摆法
    let count = 0;
    // 记录每一行的皇后所在的位置
    let queens = new Array(n).fill(0);

    const _getQueens = row => {
        // 记录该行皇后不可摆放的位置
        let visited = new Array(n).fill(false);

        // 遍历第一行到该行,皇后已经摆放的位置,遍历行
        for (let i = 0; i < row; ++i) {
            // 之前摆放过的位置不能再次摆放
            visited[queens[i]] = true;
            // 计算斜线差值,即行高
            let sub = row - i;
            // 左斜线不能再次摆放,不能越界
            if (queens[i] - sub >= 0) {
                visited[queens[i] - sub] = true;
            }
            // 右斜线不能再次摆放,不能越界
            if (queens[i] + sub <= n - 1) {
                visited[queens[i] + sub] = true;
            }
        }

        // 至此得到了该行所有不可摆放的位置

        // 尝试在剩下的可选位置中进行摆放,遍历列
        for (let i = 0; i < n; ++i) {
            // 不可摆放的位置,跳出
            if (visited[i]) {
                continue;
            }

            // 尝试该行摆放皇后,所以若此次失败,会被后续的迭代所覆盖
            queens[row] = i;

            // 递归处理下一行
            if (row < n - 1) {
                _getQueens(row + 1);
            } else {
                // 行数已至最末行,说明这种摆法成功,记录
                ++count;
            }
        }
    };

    // 从第一行开始遍历
    _getQueens(0);

    return count;
}

// 四皇后有 2 种摆法
// 八皇后有 92 种摆法
const result = func(8);

console.log(result);
/**
 *  同行:x1 = x2 
    同列:y1 = y2 
    斜线正方向(/):x1 + y1 = x2 + y2 
    斜线反方向(\):x1 - y1 = x2 - y2 
 */

function func(n) {
    const res = [];

    // n * n 的棋盘,有 2 * n - 1 条斜线
    const arr = [];
    for (let i = 0; i < n; ++i) {
        arr[i] = new Array(n).fill('+');
    }

    // 已摆放的位置
    const queens = new Array(n).fill(false);
    // 左斜线(/)已摆放的位置
    const ldiag = new Array(2 * n - 1).fill(false);
    // 右斜线(\)已摆放的位置
    const rdiag = new Array(2 * n - 1).fill(false);

    const _getQueens = row => {
        // 遍历到最末行,说明这次摆法成功
        if (row == n) {
            res.push(JSON.parse(JSON.stringify(arr)));
            return;
        }

        // 遍历该行的每一列
        for (let i = 0; i < n; ++i) {
            // 这一列或左斜线或右斜线不可摆放时,跳过
            if (queens[i] || ldiag[row + i] || rdiag[row - i]) {
                continue;
            }

            // 尝试摆放皇后的位置并记录
            arr[row][i] = 'Q';
            queens[i] = true;
            ldiag[row + i] = true;
            rdiag[row - i] = true;

            // 递归下一行
            _getQueens(row + 1);

            // 成功或失败,回退至上一步的选择,继续尝试判断该行的下一列是否可以摆放
            arr[row][i] = '+';
            queens[i] = false;
            ldiag[row + i] = false;
            rdiag[row - i] = false;
        }
    };

    // 从第一行开始遍历
    _getQueens(0);

    return res;
}

const result = func(4);

console.log(result, result.length);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值