八皇后问题 回溯算法 cpp描述

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

// 检查是否可以在 (row, col) 放置皇后
bool isSafe(const vector<int>& board, int row, int col) {
    for (int i = 0; i < row; ++i) {
        // 检查同一列和对角线冲突
        if (board[i] == col || abs(board[i] - col) == abs(i - row)) {
            return false;
        }
    }
    return true;
}

// 递归解决八皇后问题
void solveNQueens(vector<int>& board, int row, vector<vector<int>>& solutions) {
    int n = board.size();
    if (row == n) {
        solutions.push_back(board); // 找到一个解
        return;
    }

    for (int col = 0; col < n; ++col) {
        if (isSafe(board, row, col)) {
            board[row] = col; // 放置皇后
            solveNQueens(board, row + 1, solutions); // 递归处理下一行
            board[row] = -1; // 撤销选择(回溯)
        }
    }
}

// 打印棋盘
void printSolution(const vector<int>& board) {
    int n = board.size();
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (board[i] == j) {
                cout << "Q ";
            } else {
                cout << ". ";
            }
        }
        cout << endl;
    }
    cout << endl;
}

int main() {
    int n = 8; // 棋盘大小,八皇后问题固定为 8
    vector<int> board(n, -1); // 初始化棋盘,每行初始为空
    vector<vector<int>> solutions; // 存储所有解

    solveNQueens(board, 0, solutions);

    cout << "Total solutions: " << solutions.size() << endl;

    for (const auto& solution : solutions) {
        printSolution(solution);
    }

    return 0;
}

代码说明

  1. isSafe 函数:检查当前行是否可以安全地放置皇后,防止同列和对角线冲突。
  2. solveNQueens 函数:通过递归和回溯生成所有合法的棋盘布局。
  3. printSolution 函数:打印棋盘,用 Q 表示皇后,用 . 表示空位。
  4. 主函数:设置棋盘大小为 8,调用解决函数,并打印解的数量和具体布局。

运行结果

程序运行后会输出所有八皇后的解以及解的总数(92 个)。

回溯算法的特点

  1. 递归+回溯:
    • 用递归的方式遍历所有可能的棋盘布局。
    • 当发现不符合条件时,通过回溯撤销选择,继续尝试其他可能性。
  2. 剪枝优化:
    • 在放置皇后时,通过 isSafe 提前判断是否满足条件,减少无效尝试。

这段代码结构清晰,适用于任何 n 皇后问题,只需调整 n 的值即可。

有的人会问:棋盘是二维的的,为什么isSafe函数的board是个一维数组?

在这个实现中,使用一维数组 board 来表示二维棋盘,这是一种常见的优化方式。下面解释这种表示法的意义和好处:


1. 棋盘的表示方法

  • 二维棋盘通常用 n x n 的数组表示,每个位置可以是 0(空)或 1(有皇后)。
int board[8][8]; // 传统二维数组表示法

但是,这里选择了一维数组 board,其含义如下:

  • 数组的下标表示行号 (row)。
  • 数组的值表示对应行的皇后放在哪一列 (col)。
vector<int> board(n, -1);

举例: 假设 board = [1, 3, 0, 2]

  • board[0] = 1 表示第 0 行的皇后放在第 1 列。
  • board[1] = 3 表示第 1 行的皇后放在第 3 列。
  • board[2] = 0 表示第 2 行的皇后放在第 0 列。
  • board[3] = 2 表示第 3 行的皇后放在第 2 列。

相应的棋盘布局是:

. Q . .
. . . Q
Q . . .
. . Q .

2. 为什么用一维数组

  1. 简化逻辑

    • 每行只能放置一个皇后,因此不需要检查同一行是否冲突,只需记录每行皇后的位置。
    • 这样可以用 board[row] 存储第 row 行皇后的位置,而无需维护整个二维数组。
  2. 节省空间

    • 二维数组的空间复杂度是 O(n2)O(n^2)O(n2),而一维数组的空间复杂度是 O(n)O(n)O(n)。
    • 对于较大的棋盘(如 n>8n > 8n>8),这种优化非常重要。
  3. 易于实现对角线检查

    • 检查对角线冲突时,用 abs(board[i] - col) == abs(i - row) 就可以判断两皇后是否在同一对角线上。
    • 如果用二维数组实现,需要通过复杂的循环和索引来检查对角线。

3. isSafe 函数工作原理

bool isSafe(const vector<int>& board, int row, int col) {
    for (int i = 0; i < row; ++i) {
        if (board[i] == col || abs(board[i] - col) == abs(i - row)) {
            return false;
        }
    }
    return true;
}

  • 遍历 board 中之前所有行 i 的皇后位置:
    1. 列冲突: 检查 board[i] == col,即前面某一行的皇后是否在当前列。
    2. 对角线冲突: 检查 abs(board[i] - col) == abs(i - row),即前面某一行的皇后是否在当前对角线上。
  • 由于一维数组记录了每行皇后的位置,不需要额外的逻辑去检查行冲突。

4. 总结

使用一维数组代替二维棋盘的关键原因是:

  • 空间节省:从 O(n2)O(n^2)O(n2) 降到 O(n)O(n)O(n)。
  • 逻辑简化:每行最多只有一个皇后,使用 board[row] 自然表达。
  • 易于判断对角线冲突:通过简单的数学运算解决。

这种优化方式在处理类似问题(如数独、n 皇后等)中非常常见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值