回溯 - N 皇后问题


👑 N 皇后问题学习笔记(含完整 C++ 代码)


📌 题目简介

N 皇后问题是指将 N 个皇后放置在 N×N 的国际象棋棋盘上,使得它们不能相互攻击,即任意两个皇后都不能处于同一行、同一列或同一对角线上。返回所有符合要求的摆法。


🔍 解题思路:回溯法(Backtracking)

回溯框架:

  1. 从第 0 行开始尝试放置皇后;
  2. 在每一行中,遍历所有列,尝试放皇后;
  3. 如果当前位置不受攻击,就放下皇后并继续处理下一行;
  4. 如果下一行无合法选择,则撤回当前选择(回溯);
  5. 最终当所有行都放完皇后,即找到一个合法解。

🧠 冲突判断规则

放置皇后之前,需确保当前位置不与已放置的皇后在:

  • 同一列;
  • 同一主对角线(左上到右下);
  • 同一副对角线(右上到左下)上。

可以用以下方式判断冲突:

  • 列冲突:使用一个 vector<bool> cols
  • 主对角线冲突:diag1[row - col + n - 1]
  • 副对角线冲突:diag2[row + col]

🧱 完整 C++ 源码

#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;

unordered_set<int> diag1;    // row + col(主对角线)
unordered_set<int> diag2;    // row - col(副对角线)
unordered_set<int> usedCols; // 已占用的列
vector<vector<int>> solutions; // 记录所有解,每个解为一组Q所在列号

void DFS(int row, int n, vector<int> &currentPath)
{
    if (row == n)
    {
        solutions.push_back(currentPath);
        return;
    }

    for (int colIndex = 0; colIndex < n; colIndex++)
    {
        if (usedCols.count(colIndex) || diag1.count(row + colIndex) || diag2.count(row - colIndex))
        {
            continue;
        }

        // 标记当前位置
        usedCols.insert(colIndex);
        diag1.insert(row + colIndex);
        diag2.insert(row - colIndex);
        currentPath.push_back(colIndex);

        DFS(row + 1, n, currentPath);

        // 回溯
        usedCols.erase(colIndex);
        diag1.erase(row + colIndex);
        diag2.erase(row - colIndex);
        currentPath.pop_back();
    }
}

int main()
{
    int n;
    cin >> n;
    vector<int> currentPath;
    DFS(0, n, currentPath);

    for (const auto &solution : solutions)
    {
        for (int colIndex : solution)
        {
            for (int i = 0; i < n; i++)
            {
                cout << (i == colIndex ? "Q" : ".");
            }
            cout << '\n';
        }
        cout << '\n';
    }

    return 0;
}


🧪 示例输出

输入:

4

输出:

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

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

📌 关键点总结

关键点 描述
回溯法 每一层尝试在某一行放一个皇后
剪枝优化 用三个布尔数组快速判断是否冲突
时间复杂度 O(N!),因为每一行都尝试 N 个位置
空间复杂度 O(N²) 存储棋盘和标记数组

posted @ 2025-04-25 18:57  vv大人  阅读(264)  评论(0)    收藏  举报