n-皇后问题(DFS回溯)

n−皇后问题是指将 n 个皇后放在 n×n的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

1_597ec77c49-8-queens.png

现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式

共一行,包含整数 n。

输出格式

每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

1≤n≤9

输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.

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

国际象棋中的皇后可以直接攻击到她所在的行,列,斜方向上的棋子

思路:这道题也是使用DFS来求解的,关于DFS与递归的问题可以先看我的上一篇文章,有图解:排列数字(DFS深度优先搜索)-CSDN博客

一开始想的办法就是遍历枚举每一种情况,第一行第一列放皇后,之后如何,第二行第二列放皇后,之后如何,但是这样太繁琐了。

不妨每次只看一行的情况,遍历这一行上的每一列,如果当前位置可以放则放下皇后,然后进入下一行,再次枚举放皇后,直到每一行都放好了皇后,这时候便得到了一种可行的摆法,最开始是第一行第一列放皇后,得到解之后或者位置矛盾就回溯(return和if循环),第一行第二列放皇后,以此类推。

回溯则是从终止的那一列开始的(已经输出或者位置矛盾不能放),因为每一列都有一个for循环,所以当前列终止之后会自动执行上一列的for循环(如果还能循环的话),也就是我们所谓的回到上一列继续枚举,如果全部执行完毕就得到了全部结果。

这是DFS的代码:

void dfs(int u) //u表示行,输出每一行皇后的位置
{
    if( u == n ) //到最后一个位置的下一位(所有位置都填满了)
    {
        for(int i=0;i<n;i++) puts(g[i]); //输出第i行的棋盘状态,g是二维数组,这样是按行输出
        puts("");
        return;
    }
    for(int i=0;i<n;i++) //对于一行,枚举每一列的情况
    {
        //行是u,列是i
        //dg下标n-u+i,udg下标u+i,如果下标相同就说明是同一条对角线
        if(!col[i] && !dg[n+u-i] && !udg[u+i]) //如果这一列没有放过,对角线和反对角线也没有放过
        {
            g[u][i]='Q'; //在当前的位置放上皇后
            col[i] = dg[n+u-i] = udg[u+i] = true; //记录当前位置已经被使用
            dfs(u+1); //全部处理好后进入下一层
            col[i] = dg[n+u-i] = udg[u+i] = false; //回溯的时候要恢复
            g[u][i]='.';  //回溯要把这个位置的皇后去掉
        }
    }    
}
输出的时候是按行输出
if( u == n ) //到最后一个位置的下一位(所有位置都填满了)
    {
        for(int i=0;i<n;i++) puts(g[i]); //输出第i行的棋盘状态,g是二维数组,这样是按行输出
        puts("");
        return;
    }

 对于二维数组 g,可以通过 g[i] 访问到第 i 行的字符串,因为二维数组在内存中是按行存储的连续空间。

在 C++ 中,二维数组可以看作是一维数组的数组。对于 g 这样的二维数组,g[i] 表示的是第 i 个一维数组的起始地址,即第 i 行的地址。由于数组名就是该数组的首地址,因此可以直接使用 g[i] 来表示第 i 行。

其中
if(!col[i] && !dg[n+u-i] && !udg[u+i])

这个判断条件是检查在当前点g[u][i]上的竖列,正对角线和反对角线上是否有皇后(因为我们是一行一行的输入,然后按列枚举皇后在当前行的摆法,所以不用判断行上有没有其他皇后)。

dg和udg的下标可能较难理解,如果图中的点位置用(x,y)表示x行y列,那么这里的u对应行,i对应列,也就是(u,i)表示当前点的位置,这里画图可知道同一条反对角线上,u+i是一样的,而同一条正对角线上,u-i是一样的,但是因为这里用下标表示第几条正对角线,下标不能为负,所以我们加上一个n使下标为正数(加上之后不影响下标表达,比如原来正对角线的下标有-3,-1,2,7,我们加上一个n=4之后就变为1,3,6,11,实际上每个下标还是分开的能映射到对应的正对角线)

示例代码:
#include<iostream>
using namespace std;
const int N=20; //对角线是格子的两倍长
char g[N][N]; //记录棋盘状态信息
bool col[N],dg[N*2],udg[N*2]; //col列,dg记录正对角线的占用情况,udg记录负对角线的占用
int n;

void dfs(int u) //u表示行,输出每一行皇后的位置
{
    if( u == n ) //到最后一个位置的下一位(所有位置都填满了)
    {
        for(int i=0;i<n;i++) puts(g[i]); //输出第i行的棋盘状态,g是二维数组,这样是按行输出
        puts("");
        return;
    }
    for(int i=0;i<n;i++) //对于一行,枚举每一列的情况
    {
        //行是u,列是i
        //dg下标n-u+i,udg下标u+i,如果下标相同就说明是同一条对角线
        if(!col[i] && !dg[n+u-i] && !udg[u+i]) //如果这一列没有放过,对角线和反对角线也没有放过
        {
            g[u][i]='Q'; //在当前的位置放上皇后
            col[i] = dg[n+u-i] = udg[u+i] = true; //记录当前位置已经被使用
            dfs(u+1); //全部处理好后进入下一层
            col[i] = dg[n+u-i] = udg[u+i] = false; //回溯的时候要恢复
            g[u][i]='.';  //回溯要把这个位置的皇后去掉
        }
    }    
}
int main()
{

    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            g[i][j]='.';  //初始化,棋盘全部为'.'
        }
    }
    dfs(0); //从第0行开始看
    
}
关于代码运行的流程:n=4的情况

注意:不仅是return有回溯到上一列的效果,当进入递归中,上一列的for循环也是可以让当前列回溯到上一列。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/173042.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年【四川省安全员A证】复审考试及四川省安全员A证考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 四川省安全员A证复审考试根据新四川省安全员A证考试大纲要求&#xff0c;安全生产模拟考试一点通将四川省安全员A证模拟考试试题进行汇编&#xff0c;组成一套四川省安全员A证全真模拟考试试题&#xff0c;学员可通过…

Gitlab安装与操作

GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的Web服务。 可通过Web界面进行访问公开的或者私人项目。它拥有与Github类似的功能&#xff0c;能够浏览源代码&#xff0c;管理缺陷和注释。可以管理团队对仓库的…

五年程序员兼职接单的肺腑之言

不知不觉我已经参加工作&#xff0c;当一个程序员五年了&#xff0c;从一个职场菜鸟逐渐变成老油条&#xff0c;个中辛酸只有自己知道。这五年做过各种兼职接单&#xff0c;踩过不少坑&#xff0c;今天就把我在程序员接单上的一些心得体会分享给大家&#xff0c;希望能对兼职接…

【每日OJ —— 225.用队列实现栈(队列)】

每日OJ —— 225.用队列实现栈&#xff08;队列&#xff09; 1.题目&#xff1a;225.用队列实现栈&#xff08;队列&#xff09;2.解法2.1.解法讲解&#xff1a;2.1.1.算法讲解2.1.2.代码实现2.1.3.提交通过展示 1.题目&#xff1a;225.用队列实现栈&#xff08;队列&#xff0…

Jieba库——中文自然语言处理的利器

中文作为世界上最广泛使用的语言之一&#xff0c;其复杂的结构和丰富的表达方式给中文文本处理带来了挑战。为了解决这些问题&#xff0c;Python开发者开发了一系列用于处理中文文本的工具和库&#xff0c;其中最受欢迎和广泛应用的就是Jieba库。Jieba是一个开源的中文分词工具…

【SA8295P 源码分析 (三)】132 - GMSL2 协议分析 之 GPIO/SPI/I2C/UART 等通迅控制协议带宽消耗计算

【SA8295P 源码分析】132 - GMSL2 协议分析 之 GPIO/SPI/I2C/UART 等通迅控制协议带宽消耗计算 一、GPIO 透传带宽消耗计算二、SPI 通迅带宽消耗计算三、I2C 通迅带宽消耗计算四、UART 通迅带宽消耗计算系列文章汇总见:《【SA8295P 源码分析 (三)】Camera 模块 文章链接汇总 -…

基于Vue+SpringBoot的考研专业课程管理系统

项目编号&#xff1a; S 035 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S035&#xff0c;文末获取源码。} 项目编号&#xff1a;S035&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 考研高校模块2.3 高…

ubuntu linux C/C++环境搭建

目录 前言 1.1 vim安装与配置 ​编辑 1.2 vim配置 1.3 gcc g编译器的安装 与gdb调试器的安装 1.4 写个C/C程序测试一下 1.6 vscode安装 1.7 vscode插件下载​编辑 前言 在开始C之前&#xff0c;我们需要搭建好C的开发环境&#xff0c;我这里使用的操作系统是ubuntu Linux&a…

Linux难学?大神告诉你,Linux到底该怎么自学!

文章目录 Part.1Part.2Part.3写作末尾 知乎上有一条热门问答&#xff0c;问题是“Linux为什么那么难&#xff1f;” 从问题来看&#xff0c;提问者还处在初学阶段。但他显然受困于 Linux 环境基本操作的问题&#xff0c;对操作系统本身的原理还不熟悉&#xff0c;并且对命令行工…

LeetCode热题100——动态规划

动态规划 1. 爬楼梯2. 杨辉三角3. 打家劫舍 1. 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; // 题解&#xff1a;每次都有两种选择&#xff0c;1或者2 int climbStairs(int n) {if (n …

Spring Cloud Alibaba Sentinel 简单使用

Sentinel Sentinel 主要功能Sentinel 作用常见的流量控制算法计数器算法漏桶算法 令牌桶算法Sentinel 流量控制Sentinel 熔断Sentinel 基本使用添加依赖定义资源定义限流规则定义熔断规则如何判断熔断还是限流自定义 Sentinel 异常局部自定义异常全局自定义异常系统自定义异常…

网工内推 | 字节原厂,正式编,网络工程师,最高30K*15薪

01 字节跳动 招聘岗位&#xff1a;网络虚拟化高级研发工程师 职责描述&#xff1a; 1、负责字节跳动虚拟网络产品的研发&#xff0c;包括但不局限于网络VPC、NAT、LB负载均衡等&#xff1b; 2、负责字节跳动网络基础平台的研发&#xff0c;包括但不局限于网络控制面系统、容器…

JS--localStorage设置过期时间的方案(有示例)

原文网址&#xff1a;JS--localStorage设置过期时间的方案(有示例)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍如何使用localStorage设置数据的过期时间。 问题描述 localStorage是不支持设置过期时间的&#xff0c;cookie虽然支持设置过期时间但它存的数据量很小。所…

Redis从入门到精通(二)- 入门篇

文章目录 0. 前言1. 入门篇[【入门篇】1.1 redis 基础数据类型详解和示例](https://icepip.blog.csdn.net/article/details/134438573)[【入门篇】1.2 Redis 客户端之 Jedis 详解和示例](https://icepip.blog.csdn.net/article/details/134440061)[【入门篇】1.3 redis客户端之…

打码平台之图鉴的使用步骤

打码平台之图鉴 背景&#xff1a; ​ 今天给大家推荐一个我一直使用的验证码识别平台&#xff0c;图鉴&#xff0c;我没有收费&#xff0c;我只是觉得这个网站使用方便&#xff0c;支持验证码种类多&#xff0c;好了&#xff0c;话不多说&#xff0c;上教程&#xff01; 注册…

小程序制作(超详解!!!)第十六节 小程序的基本架构

1.题目描述 创建一个包含:首页、教学、科研、资讯和关于我们5个标签的小程序&#xff0c;每个标签都有对应的页面、图标和标签文字&#xff0c;点击某个标签将切换到对应的页面&#xff0c;同时该标签的图标和文字颜色都会发生变化页面的标题也发生相应的变化&#xff0c;而其…

数字IC基础:有符号数和无符号数的加减运算

相关阅读 数字IC基础https://blog.csdn.net/weixin_45791458/category_12365795.html?spm1001.2014.3001.5482 首先说明&#xff0c;本篇文章并不涉及补码运算正确性的证明&#xff0c;仅是对补码运算在有符号数和无符号数中运行进行讨论。 补码运算最大的作用在于消除计算机…

RabbitMQ 基础操作

概念 从计算机术语层面来说&#xff0c;RabbitMQ 模型更像是一种交换机模型。 Queue 队列 Queue&#xff1a;队列&#xff0c;是RabbitMQ 的内部对象&#xff0c;用于存储消息。 RabbitMQ 中消息只能存储在队列中&#xff0c;这一点和Kafka相反。Kafka将消息存储在topic&am…

2023年【T电梯修理】考试题及T电梯修理考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 T电梯修理考试题是安全生产模拟考试一点通总题库中生成的一套T电梯修理考试报名&#xff0c;安全生产模拟考试一点通上T电梯修理作业手机同步练习。2023年【T电梯修理】考试题及T电梯修理考试报名 1、【多选题】GB/T1…

什么是PyQt?

什么是Qt? Qt是一个著名的跨平台C图形用户界面应用程序开发框架。它由Qt公司开发,于1995年首次发布。Qt支持各种桌面,嵌入式和移动平台。 Qt的特点包括: 跨平台支持:Qt应用程序可以编译到多种平台运行,包括Windows,Mac,Linux,Android,iOS等。这大大简化了跨平台应用程序的开…