news 2026/8/20 1:05:52

“N皇后”问题解法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
“N皇后”问题解法

C++实现N皇后问题(回溯法详解+OJ适配)

一、核心问题分析

  • 不同行:由于每个皇后占一行,可简化为“逐行放置”(每行仅放一个皇后)

  • 不同列:同一列不能有两个皇后

  • 不同对角线:主对角线(x-y为常数)和副对角线(x+y为常数)不能有两个皇后

二、解题思路:回溯法

N皇后问题的核心解法是回溯法,本质是“尝试-回溯-再尝试”的暴力搜索优化思路:

  1. 初始化n×n的空棋盘(用'.'表示空位置);

  2. 逐行放置皇后:第i行放置一个皇后,标记其攻击范围(行、列、对角线);

  3. 递归处理下一行,重复步骤2;

  4. 若递归到第n行(所有皇后放置完成),则收集当前棋盘作为有效解;

  5. 回溯:撤销当前皇后的放置和攻击范围标记,尝试当前行的下一个列位置;

  6. 遍历所有可能的位置,直到收集完所有有效解。

关键优化:用“数字字符标记不同皇后的攻击范围”,确保回溯时能精准恢复棋盘状态,避免不同皇后的攻击范围混淆。

三、完整代码实现(OJ适配版)

以下代码已封装为LeetCode/OJ要求的Solution类,入口函数为solveNQueens(int n),可直接复制提交:

#include <iostream> #include <vector> #include <string> using namespace std; void conver(vector<vector<char>> a, vector<vector<string>>& b) { vector<string> temp; for ( auto char_row : a) { for (char& c : char_row) { if (c != 'Q' && c != '.') { c = '.'; } } string str(char_row.begin(), char_row.end()); temp.push_back(str); } b.push_back(temp); } void change(int x, int y, char c, vector<vector<char>>& a,int n) { for (int i = 0; i < n; i++) { if (a[x][i] == '.') a[x][i] = c; if(a[i][y]=='.') a[i][y] = c; } for (int i = -(n - 1); i <= n - 1; i++) { if (x + i < n && x + i >= 0 && y + i >= 0 && y + i < n&&a[x+i][y+i]=='.') a[x + i][y + i] = c; if (x - i < n && x - i >= 0 && y + i < n && y + i >= 0&&a[x-i][y+i]=='.') a[x - i][y + i] = c; } } void restore(char c, int x, int y,int n,vector<vector<char>>&a) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (a[i][j] == c) { a[i][j] = '.'; } } } } void queen( int num, int n, vector<vector<char>>& a, vector<vector<string>>& b) { if (num == n) { conver(a, b); return; } for (int i= 0; i < n; i++) { if (a[num][i] == '.') { change(num, i, '0'+num, a, n); a[num][i] = 'Q'; queen(num + 1, n, a, b); restore('0'+num, num, i, n, a); a[num][i] = '.'; } } return; } int main() { int n = 4; vector<vector<char>> a(n, vector<char>(n)); vector<vector<string>> b; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { a[i][j] = '.'; } } int num = 0; queen(num , n, a, b); for (const auto& str_arr : b) { for (const auto& str : str_arr) { cout << str<<endl; } cout <<endl<<"---------------"<< endl; } }

四、核心代码模块解析

1. 主函数:int main()

作用:OJ的统一入口,负责初始化棋盘、调用递归函数、返回最终结果。

  • 初始化n×n的棋盘a,所有位置设为'.'(空);

  • 创建b存储所有有效解法(二维字符串数组,每个子数组是一个完整的棋盘);

  • 调用核心递归函数queen,从第0行(num=0)开始放置皇后。

2. 核心递归函数:queen(int num, int n, vector<vector<char>>& a, vector<vector<string>>& b)

作用:实现回溯法的核心逻辑——逐行放置皇后、递归、回溯。

  • 终止条件num == n,表示已处理完第0~n-1行(所有皇后放置完成),调用conver转换结果并收集;

  • 逐行遍历num表示当前处理的行,遍历当前行的所有列(i从0到n-1);

  • 放置皇后:若当前位置a[num][i] == '.'(空),则:

    • '0' + num生成当前皇后的专属标记(如第0行皇后用'0',第1行用'1');

    • 调用change标记攻击范围;

    • 将当前位置设为'Q'(放置皇后);

    • 递归处理下一行(num+1);

    • 回溯:调用restore恢复攻击范围标记,将当前位置设回'.'(撤销皇后)。

3. 攻击范围标记:change(int x, int y, char c, vector<vector<char>>& a, int n)

作用:标记皇后(x,y)的攻击范围(行、列、主对角线、副对角线),用字符c(专属标记)标记,避免与其他皇后混淆。

  • 标记当前行:遍历第x行所有列,空位置设为c

  • 标记当前列:遍历第y列所有行,空位置设为c

  • 标记主对角线:x+i, y+i(x-y为常数),需判断边界(0≤nx<n,0≤ny<n);

  • 标记副对角线:x-i, y+i(x+y为常数),同样判断边界。

4. 回溯恢复:restore(char c, int x, int y, int n, vector<vector<char>>& a)

作用:撤销当前皇后的攻击范围标记——将所有标记为c的位置恢复为'.',确保回溯后棋盘状态正确。

关键:由于每个皇后的标记c是唯一的('0'~'n-1'),恢复时不会影响其他皇后的标记。

5. 结果转换:conver(vector<vector<char>> a, vector<vector<string>>& b)

作用:将标记后的棋盘转换为OJ要求的输出格式——过滤攻击范围标记('0'~'n-1'),仅保留'Q'(皇后)和'.'(空)。

注意:参数a采用值传递,避免修改原棋盘的状态(不影响后续回溯)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 19:36:47

75亿去买理财,反而是摩尔线程最“诚实”的一刻

当所有人都在嘲讽摩尔线程“75 亿不搞研发&#xff0c;跑去吃银行利息”的时候&#xff0c;我反而觉得&#xff0c;这可能是它目前为止做过的最良心、也最真实的一次选择。原因很简单——如果你真正看懂了 A 股过去 20 年的历史&#xff0c;就会明白&#xff0c;钱老老实实躺在…

作者头像 李华
网站建设 2026/8/20 14:12:50

感悟闲聊人生

1.在工作中 尽量使用自己能力范围内没做过的东西&#xff0c;提升自己的技能。 2.如果是自己的东西&#xff0c;怎么简单怎么来&#xff0c;重要的是第一版&#xff0c;但是不能因为求速度而降低质量&#xff0c;要确确实实满足了你某个功能点&#xff0c;解决了你某个需求。 3…

作者头像 李华
网站建设 2026/8/20 3:34:26

GeoServer 跨域问题解决方案

转载于博客园&#xff1a;【开发问题】GeoServer 跨域问题解决方案 一、进入 geoserver 目录下的 lib 文件夹&#xff0c;将其中的 jetty-servlets-9.4.57.v20241219.jar和jetty-util-9.4.57.v20241219.jar&#xff08;版本不一定是9.4.57.v20241219&#xff0c;前缀一样就好&…

作者头像 李华
网站建设 2026/8/20 18:41:36

java实现自定义注解导出excel

结合 Spring AOP (面向切面编程) 和 Alibaba EasyExcel。 EasyExcel 是阿里巴巴开源的工具&#xff0c;相比 Apache POI&#xff0c;它极其节省内存&#xff08;基于流式读写&#xff09;&#xff0c;并且对注解支持非常好。1. 引入依赖 (Maven) 在 pom.xml 中引入 EasyExcel 和…

作者头像 李华
网站建设 2026/8/19 6:54:52

上传文档生成PPT前必看:实测多款工具后的经验总结

年终总结难&#xff1f;AI办公工具来救场每到年终&#xff0c;职场人就开始为年终总结报告发愁。熬夜加班改报告是常有的事&#xff0c;好不容易搭好的框架&#xff0c;内容却混乱不堪&#xff0c;毫无逻辑。设计方面更是让人头大&#xff0c;缺乏灵感&#xff0c;做出来的报告…

作者头像 李华
网站建设 2026/8/19 16:16:43

测试团队敏捷转型实施路径:以思维、流程、能力、价值为核心的四重变革

测试团队的敏捷转型并非单纯追求 “更快测试”&#xff0c;而是通过 “思维转变、流程重构、能力升级、价值可视化” 的系统性变革&#xff0c;实现 “更聪明地构建质量”&#xff0c;最终达成 “不做交付瓶颈、成为质量推动者、持续交付高价值软件” 的核心目标。结合与基线管…

作者头像 李华