回溯算法之N皇后问题

article/2025/10/6 0:55:28

问题描述

什么是皇后问题(有一定了解可以直接跳过这个部分看求解部分哦)

八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。计算机发明后,有多种计算机语言可以编程解决此问题。
八皇后问题

一起看看经典教材 计算机算法设计与分析 对该问题的描述:

  • 在 n × n 棋盘上放彼此不受攻击的n个皇后。
  • 按照国际象棋规则,皇后可以攻击 同行、同列、同一斜线 的棋子。
  • 等价于在 n × n 格的棋盘上放置 n 个皇后,任何 2 个皇后不放在 同一行同一列同一斜线 上。

解题思路

由于皇后的位置受到上述三条规则约束,我们必须通过一些技术手段来判断当前皇后的位置是否合法。

1.皇后的编号从 0 ~ N - 1 (N表示皇后的数量),这样编号的想法很简单:数组下标从0开始(这样方便后续对其位置的说明)。

2.使用一维数组 putInf 对每一行皇后的存放位置进行保存,因此得到解向量 (putInf[0], putInf[1], putInf[3], … , putInf[N - 1]),putInf[i] 表示第 i 个皇后被放置到了第 putInf[i] + 1 列上(putInf数组中存储的是列号,范围为 0 ~ N - 1);

3.第二个条件:各皇后不同列, N 皇后放在 N x N 的棋盘上,那么每一列最多且必须放置一个皇后,这里我用了一个 used数组 对每一列的摆放情况进行记录, used[i] = true 表示 第 i 列 已经放置了皇后,used[i] = false 表示第i列暂未放置皇后,这样我们可以保证不在一列上放置多个皇后,也就能满足 各皇后不同列 的规则。

4.各皇后不能处于同一对角线位置假设两皇后位置坐标分别为(i, j) 、(l, k),那么根据直线斜率公式:

  • (i - l) / (j - k) = 1 求解得 i - l == j - k ①
  • (i - l) / (j - k) = -1 求解得 i - l == k - j ②
    这两种情况出现时表明处于同一对角线,那么要满足摆放规则就必须满足
    | i - l | != | j - k | (“| |” 表示绝对值)

解空间树

解空间树(排列树)

实现代码

#include <iostream>
#include <vector>
using namespace std;#define N 4 //N皇后
vector<int> putInf;//每一行皇后的置放位置情况
//不同行 不同列 不同斜线 |ri - rj| != |ci - cj| 第1行与
vector<int> used(N, 0);//每一列只能有一个皇后,记录每一列的状态
vector<vector<int>> ans;//存储可行方案
int curRow = 0;//当前待放皇后的行数/*            正置放皇后行↓ 置放列↓              */
bool judgeLegalPut(int& curRow, int col) {//判断在curRow行的col列放置皇后是否合法for (int i = curRow - 1; i >= 0; i--) {//我们的解空间树已经去除一行一列置放相同元素//(每一个皇后被放在不同行以及不同列)的情况//因此我们只需要判断皇后是否成斜线即可if (curRow - i == abs(col - putInf[i])) {//当前位置与之前的皇后处于同一斜线上return false;}}return true;
}void queensAssign(int curRow) {if (curRow >= N) {//递归到叶子节点,递归结束,收集结果ans.push_back(putInf);return;}//i : 当前行皇后准备放的列数for (int i = 0; i < N; ++i) {//curRow行i列的位置if (used[i]) continue;//位置被使用过,直接跳过 //这样满足了不处于同一列的显条件 类似于全排列if (judgeLegalPut(curRow, i)) {//当前位置置放与之前不冲突 将皇后加入used[i] = true;putInf.push_back(i);queensAssign(curRow + 1);used[i] = false;//撤销之前的状态putInf.pop_back();}}
}void printChessBoard(vector<int>& vec) {//输出模拟棋盘cout << endl;for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {if (j != vec[i])cout << "○";elsecout << "●";}cout << endl;}cout << endl;
}
/// <author>
/// nepu_bin
/// <博客域名>
/// bincode.blog.csdn.net
int main() {queensAssign(0);int n = 1;cout << N << "皇后问题,方案如下:\n" << endl;for (vector<vector<int>>::iterator it = ans.begin(); it != ans.end(); it++) {cout << "第" << n++ << "种放置方案, 皇后被放于 " << endl;for (int i = 0; i < it->size(); i++) {cout << (*it)[i] + 1 << "  ";}cout <<"列" << endl;cout << endl << "棋盘布局如下↓" << endl;printChessBoard(*it);}return 0;
}

运行效果

四皇后问题运行截图:
四皇后求解

通过修改宏定义 N 可以得到不同数量皇后问题的解答~~~
八皇后求解(部分解):
八皇后求解

子集树与排列树

附上子集树 and 排列树的定义在这里插入图片描述
在这里插入图片描述
在了解过该问题之后便可以开始着手力扣上的N皇后问题,在这里贴一下实现代码:

LeetCode必刷经典: n 皇后问题

n 皇后问题,研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

链接:https://leetcode-cn.com/problems/n-queens
思路与上面完全一致,直接上实现代码:

class Solution {
public:vector<vector<string>> res;vector<int> put;//记录每个皇后放置的位置:i行put[i]列vector<string> solution;vector<bool> haveQ;//记录每一列位置上皇后的摆放情况//推导解空间树: 排列树、解集树//N皇后问题为排列树结构,每个皇后都需要放置 depth变量记录当前正摆放皇后的位置void dfs(int depth, int& n) {if (depth >= n) {res.push_back(solution);return;//此时已经完成所有皇后的摆放}for (int i = 0; i < n; i++) {//i表示列值if (haveQ[i]) continue;//当前列已经有皇后haveQ[i] = true;solution[depth][i] = 'Q';put[depth] = i;int j;//当前列无皇后,试探性摆放for (j = 0; j < depth; j++) {//检测前 depth - 1行是否发生冲突if (abs(j - depth) == abs(put[j] - i))break;}if (j >= depth) {//检测通过,继续深入dfs(depth + 1, n);}//检测失败,撤销之前的操作haveQ[i] = false;solution[depth][i] = '.';}}vector<vector<string>> solveNQueens(int n) {if (n == 1) return { {"Q"} };string str;str.assign(n, '.');//初始化n个'.'的字符串//保证横行纵行、斜线都不存在皇后//abs(y - cury) = abs(i - curi)haveQ.resize(n, false);solution.resize(n, str);put.resize(n, -1);//初始化3个容器dfs(0, n);return res;}
};

在这里的巧妙之处是:

  1. 利用了循环的顺序性消除了第一层限制: 同一行中不可以存在两个皇后,由于是顺序遍历,依次摆放皇后且每次只放置一个,因此该条件我们很容易实现。
  2. 第二个条件是同一列上不可以有两个及以上的皇后,在代码中使用了put数组,记录了每个皇后的摆放位置,利用了哈希映射的原理(put数组的下标( 0~put.size - 1) 对应着每个皇后,下标对应存储的值则表示了此位皇后摆放在了哪一列,打个比方: 下标i表示了第i位皇后(假设皇后的编号从零开始), put[i]则表示第i位皇后被放在了put[i] 列;这么做的好处是为了实现有哈希表一样的查询效率O(1)。
  3. 第三条限制则是在回溯算法的核心部分体现:
//当前列无皇后,试探性摆放for (j = 0; j < depth; j++) {//检测前 depth - 1行是否发生冲突if (abs(j - depth) == abs(put[j] - i))break;}if (j >= depth) {//检测通过,继续深入dfs(depth + 1, n);}//检测失败,撤销之前的操作haveQ[i] = false;solution[depth][i] = '.';

在模拟放置皇后之后进行了检查,通过与之前摆放的皇后位置比较是否出现在一条斜线上,若存在,则不在继续往下深入递归。
在这里插入图片描述


http://chatgpt.dhexx.cn/article/dJjIQFi5.shtml

相关文章

N皇后问题(分支限界法)

问题描述&#xff1a; 在 n * n 格的棋盘上放置彼此不受攻击的 n 个皇后。按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题等价于在 n * n 的棋盘上放置 n 个皇后&#xff0c;任何 2个皇后不放在同一行或同一列或同一斜线上…

N皇后问题详解

文章目录 一、题目描述二、题目解析&#xff08;1&#xff09;思考一(集合回溯)&#xff08;2&#xff09;思考二(数组深度递归)&#xff08;3&#xff09;思考三(位运算) 一、题目描述 N 皇后问题是指在 n * n 的棋盘上要摆 n 个皇后&#xff0c; 要求&#xff1a;任何两个皇…

3.2 回溯法—N皇后问题

1. 问题描述 在 n n n\times n nn的棋盘上摆放 n n n个皇后&#xff0c;使任意两个皇后都不能处于同一行、同一列或同一斜线上 2. 问题分析 下以求解4皇后问题为例&#xff0c;分析4皇后问题的排列树以及回溯过程&#xff1a; 搜索及回溯过程&#xff1a; 解空间树&#…

【经典算法】N皇后问题

✨前言✨ N皇后问题经典的解决方案是暴力递归&#xff0c;其时间复杂度是O(2^n),因此常用来测试计算机的算力。今天我会给大家带来经典方法的详解&#xff0c;也会给大家展示N皇后优化后的大神解法。做一道经典题目&#xff0c;来一场思维旅行。 目录 ✨前言✨ &#x1f4a1;…

n皇后问题(回溯法)(超易理解)(详细注释)

问题&#xff1a; N皇后问题是一个经典的问题&#xff0c;描述如下&#xff1a; 在的方格棋盘需要放置N个皇后&#xff0c;使得它们不相互攻击&#xff0c; 即任意2个皇后不允许处在同一排&#xff0c;同一列&#xff0c;也不允许处在与棋盘边框成45度角的斜线上。 对于给定的N…

n皇后问题(回溯法)

目录 1.问题描述 2.问题分析 3.完整源码 1.问题描述 八皇后问题是十九世纪著名的数学家高斯于1850年提出的。问题是:在88的棋盘上摆放八个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上。可以把八皇后问题扩展到n皇后问…

【浏览器跨域】跨域访问接口浏览器怎么设置?

window系统 1、右键浏览器&#xff0c;点击属性 2、找到目标&#xff0c;目标输入框内容最后面加上 --disable-web-security --user-data-dirC:\MyChromeDevUserData&#xff0c;–user-data-dir 3、点击确定 4、重新打开浏览器即可 mac系统 终端输入&#xff0c;重新打开浏…

AJAX跨域访问(不同域之间相互访问)

目录 一、跨域&#xff1a;二、同源策略&#xff1a;三、解决Ajax跨域问题的方案&#xff1a;方案一&#xff1a;设置响应头方案二&#xff1a;jsonp方案三&#xff1a;jQuery封装jsonp方案四&#xff1a;代理机制&#xff08;httpclient&#xff09;方案五&#xff1a;nginx反…

nginx配置图片跨域访问

在server段中添加红框内的图片跨域内容 参数 location ~* .*.(gif|jpg|jpeg|png|bmp|swf)$ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS…

浏览器跨域访问限制

一.什么是跨域 广义的跨域&#xff1a; (1) 资源跳转&#xff1a;A链接、重定向、表单提交 (2) 资源嵌入&#xff1a;<link>、<script>、<img>、<frame>等dom标签&#xff0c;还有样式中background:url()、font-face()等文件外链 (3) 脚本请求&#x…

Django 多方式实现跨域访问

一、什么是跨域 1.1 跨越介绍 跨域&#xff0c;是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的&#xff0c;是浏览器对JavaScript实施的安全限制。 这里说明一下&#xff0c;无法跨域是浏览器对于用户安全的考虑&#xff0c;如果自己写个没有同源策略的浏览…

Flask中跨域访问的实现

在我们访问不同的服务器时&#xff0c;就会涉及到了跨域的问题。因为不同域名之间是无法进行交流的&#xff0c;然后跨域就打破了这种规则的限制。说起Flask中的跨域&#xff0c;就不得不提到CORS组件了&#xff0c;相信大家在其它框架中也见过了它的身影。下面我们就跨域问题和…

Tomcat设置允许跨域访问

开发React项目时前端通过axios向后端代码发起请求调试的时候由于后端代码运行在8080端口而React项目运行在3000端口导致浏览器的同源策略禁止跨域请求&#xff0c;因此需修改Tomcat配置文件web.xml以开放跨域访问。 在tomcat的web.xml文件末尾加上&#xff1a; <filter>…

express 允许跨域访问

设置Express const express require(express) const bodyParser require(body-parser)const app express() // 处理参数 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false }));// 设置允许跨域访问该服务 app.all(*, function (req, res, nex…

tomcat 设置允许跨域访问

既然想到使用tomcat进行跨域的设置&#xff0c;而不使用在项目中设置header来解决&#xff0c;说明你也是tomcat 下的资源需要做跨域处理吧&#xff1f;这也是一个统一的允许跨域设置&#xff0c;tomcat下的所有请求都将放开&#xff0c;请注意。 具体步骤&#xff1a; 1.首先…

Chrome浏览器 设置跨域访问

由于浏览器的安全策略&#xff0c;不同域名的访问会被限制&#xff0c;导致不能访问 chorme或者360都可以使用下列设置来解决这个问题&#xff1a; &#xff08;1&#xff09;在电脑上新建一个目录&#xff0c;例如&#xff1a;C:\MyChromeEnvUserData &#xff08;2&#x…

tomcat9配置允许跨域访问

目录 一、问题描述二、解决办法三、程序截图 一、问题描述 最近在用Tomcat9最新版本部署程序的时候&#xff0c;发现其他服务访问我的静态资源的时候出现了跨域访问&#xff0c;结果很明显被拦截了&#xff0c;之前的文章介绍了Tomcat8.5的跨域配置&#xff0c;但是研究发现&a…

html的页面怎样直接跨域访问,【HTML】iframe跨域访问问题

概述 本地同一浏览器访问本地HTML文件和访问服务器端HTML文件,本地Iframe没有自适应高度,而服务器端的Ifrane自适应了高度。 1.问题重现: Chrome 版本 41.0.2272.101 (64-bit) OS:Win8.1 Chrome访问服务器端HTML文件呈现的结果 Chrome访问本地HTML文件呈现的结果 本地访问的…

vue跨域访问

vue跨域访问的问题&#xff08;基于允许跨域访问的前提&#xff09; 找到“ vue.config.js"&#xff0c;有些不是vue.config.js命名&#xff0c;也就是找到重写路径的js文件。重写一下自己要替换的路径&#xff0c;在原有基础上加一个 新建一个dev.js文件内容如下&#…

关于跨域访问

1 概念 2 案例 3 解决 4 实现 5 什么是跨域问题 示意图 1 概念 跨域访问&#xff0c;是指从一个域名的网页去请求另一个域名的资源。比如从 www.baidu.com 页面去请求 ww w.google.com 的资源。 但是一般情况下不能这么做跨域访问&#xff0c;因为有浏览器的 “ 同源策略…