代码随想录阅读笔记-回溯【N皇后】

题目

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

  • 输入:n = 4
  • 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
  • 解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

  • 输入:n = 1
  • 输出:[["Q"]]

思路 

都知道n皇后问题是回溯算法解决的经典问题,但是用回溯解决多了组合、切割、子集、排列问题之后,遇到这种二维矩阵还会有点不知所措。

首先来看一下皇后们的约束条件:

  1. 不能同行
  2. 不能同列
  3. 不能同斜线

确定完约束条件,来看看究竟要怎么去搜索皇后们的位置,其实搜索皇后的位置,可以抽象为一棵树。下面我用一个 3 * 3 的棋盘,将搜索过程抽象为一棵树,如图:

51.N皇后

从图中,可以看出,二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。那么我们用皇后们的约束条件,来回溯搜索这棵树,只要搜索到了树的叶子节点,说明就找到了皇后们的合理位置了

回溯三部曲

1、递归函数参数

我依然是定义全局变量二维数组result来记录最终结果。

参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。

代码如下:

vector<vector<string>> result;
void backtracking(int n, int row, vector<string>& chessboard) {

2、递归终止条件

在如下树形结构中: 

51.N皇后

可以看出,当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了。

代码如下:

if (row == n) {
    result.push_back(chessboard);
    return;
}

3、单层搜索的逻辑

递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。每次都是要从新的一行的起始位置开始搜,所以都是从0开始。

for (int col = 0; col < n; col++) {
    if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
        chessboard[row][col] = 'Q'; // 放置皇后
        backtracking(n, row + 1, chessboard);
        chessboard[row][col] = '.'; // 回溯,撤销皇后
    }
}

验证棋盘是否合法

按照如下标准去重:

  1. 不能同行
  2. 不能同列
  3. 不能同斜线 (45度和135度角)

代码如下:

bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}

在这份代码中,细心的同学可以发现为什么没有在同行进行检查呢?因为在单层搜索的过程中,每一层递归,只会选for循环(也就是同一行)里的一个元素,所以不用去重了。

那么按照这个模板不难写出如下C++代码:

class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
    if (row == n) {
        result.push_back(chessboard);
        return;
    }
    for (int col = 0; col < n; col++) {
        if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
            chessboard[row][col] = 'Q'; // 放置皇后
            backtracking(n, row + 1, chessboard);
            chessboard[row][col] = '.'; // 回溯,撤销皇后
        }
    }
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}
public:
    vector<vector<string>> solveNQueens(int n) {
        result.clear();
        std::vector<std::string> chessboard(n, std::string(n, '.'));
        backtracking(n, 0, chessboard);
        return result;
    }
};
  • 时间复杂度: O(n!)
  • 空间复杂度: O(n)

下面给出笔者自己的c++代码与大家分享,思路大体和上面的差不多,但是也不尽相同,其中我用一个q_count代表当前已经放置的皇后数目,同时也代表了当前回溯树的深度,另外最主要的差别就是我用一个pos数组代表了棋盘每一行上皇后所在位置,最后输出结果时就需要用gouzao函数将这些位置信息转换成vector<string>虽然多了一个步骤,但是这对于判断当前位置放置皇后是否合法会有很大程度上的精简,代码如下:

class Solution {
public:
    vector<vector<string>> result;
    vector<string> path;
    int q_count=0;
    int pos[10] = {-1};
    bool is_ok(int x,int y)
    {
        for (int i = 0 ; i < x ; i++){
            if(y == pos[i] || abs(x-i) == abs(y - pos[i]))
            return false;
        }
        return true;
    }
    void gouzao(int n)
    {
        path.clear();
        string tmp;
        for(int i = 0 ; i< n ; i++)
        {
            for(int j = 0 ; j < n ; j++)
            {
                if(j == pos[i]) tmp+='Q';
                else tmp+='.';
            }
            if(!tmp.empty()) path.push_back(tmp);
            tmp.clear();
        }
        result.push_back(path);
    }
    void backtracing(int n)
    {
        if(q_count == n)
        {
            gouzao(n); 
            return;
        }

        for(int i = 0 ; i < n ; i++)
        {
            if(!is_ok(q_count,i)) continue;
            pos[q_count++] = i;
            backtracing(n);
            q_count--;
            pos[q_count] = -1;
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        backtracing(n);
        return result;
    }
};

可以看到我这里判断合法的语句只有一句if(y == pos[i] || abs(x-i) == abs(y - pos[i])),其中x和y代表当前位置的坐标,pos数组代表每一行中皇后的位置索引,具体判断方法就是遍历之前已经放置过的每一行,如果遇到y == pos[i],既在第i行的y列已经有皇后了,那么不合法,或者abs(x-i) == abs(y - pos[i]),既当前位置的斜向也已经有皇后则也不合法,后一个条件需要大家理解一下,两个坐标在同一个斜线代表两坐标连线与水平的夹角为45度,通过斜率公式便可以写出上面的条件,不理解的可以在纸上画一画加深理解。

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

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

相关文章

【笔记】Telephony SIM SPN及运营商名称显示数据来源介绍

来源介绍 网络名称显示 来源及优先级&#xff08;高到低&#xff09; SourceCommentEnhanced Operator Name String(Eons) 名称信息存放&#xff1a; EF_PNN(PLMN Network Name, fid: 6FC5) &#xff1a;LAC和EF_PNN中的Record Identifier EF_OPL(Operator PLMN List, fid: 6FC…

数字工厂系统的开发

数字工厂系统&#xff08;Digital Factory System&#xff0c;DFS&#xff09;是基于数字孪生技术&#xff08;Digital Twin Technology&#xff09;和信息物理融合系统&#xff08;Cyber-Physical System&#xff0c;CPS&#xff09;构建的&#xff0c;用于仿真、分析和优化制…

【服务器部署篇】Linux下Nginx的安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0c;产…

[Collection与数据结构] 二叉树(三):二叉树精选OJ例题(下)

1.二叉树的分层遍历 OJ链接 上面这道题是分层式的层序遍历,每一层有哪些结点都很明确,我们先想一想普通的层序遍历怎么做 /*** 层序遍历* param root*/public void levelOrder1(Node root){Queue<Node> queue new LinkedList<>();queue.offer(root);while (!qu…

中文编程入门(Lua5.4.6中文版)第十二章 Lua 协程 参考《愿神》游戏

在《愿神》的提瓦特大陆上&#xff0c;每一位冒险者都拥有自己的独特力量——“神之眼”&#xff0c;他们借助元素之力探索广袤的世界&#xff0c;解决谜题&#xff0c;战胜敌人。而在提瓦特的科技树中&#xff0c;存在着一项名为“协同程序”的高级秘术&#xff0c;它使冒险者…

今天刷两题(day2)

题目一&#xff1a;最长公共前缀 题目描述&#xff1a; 给你一个大小为 n的字符串数组 strs &#xff0c;其中包含n个字符串 , 编写一个函数来查找字符串数组中的最长公共前缀&#xff0c;返回这个公共前缀。输入输出描述&#xff1a; 输入&#xff1a;"abca","…

照片光晕光学特效模拟调色Boris FX Optics 2024 mac下载安装教程

Boris FX Optics 2024 Mac版是一款照片光晕光学特效模拟调色软件&#xff0c;旨在模拟光学相机滤镜&#xff0c;专用镜头&#xff0c;胶卷和颗粒&#xff0c;镜头光晕&#xff0c;光学实验室处理&#xff0c;色彩校正以及自然光和摄影效果。用户可以通过应用光学并从160个滤镜和…

Day43:LeedCode 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果…

图书管理系统概述

自友图书馆管理系统解决方案适用于中小学、大中专院校以及企事业单位中小型图书馆的自动化管理需求&#xff0c;其功能覆盖了图书馆自动化集成管理业务流程所包括的所有环节。《图书馆管理系统》首先应该按照我国图书馆行业通用CNMARC格式及《中图法第四版》行业标准开发而成,支…

计算机网络-IS-IS基础概念二

前面已经学习了IS-IS的定义、组成、NET地址标识以及路由器级别分类等&#xff0c;今天继续学习IS-IS基础概念知识。 参考链接&#xff1a;IS-IS路由协议基础概念 一、IS-IS支持的网络类型 IS-IS会自动根据接口的数据链路层封装决定该接口的缺省网络类型&#xff0c; IS-IS支持两…

【Go语言快速上手(二)】 分支与循环函数讲解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Go语言专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; Go快速上手 1. 前言2. 分支与循环2.1…

中国隧道空间分布

中国隧道空间分布数据&#xff0c;包含2020年全国大部分地区16000余条隧道分布点位数据&#xff0c;数据包括市名称、区县名称、隧道名称和隧道经纬度。数据包含shp和EXCEl两种格式&#xff0c;部分隧道空间位置有偏移。 欢迎大家关注、收藏和留言&#xff0c;如果您想要什么数…

【GPT-4最新研究】GPT-4与科学探索:揭秘语言模型在科学领域的无限可能

各位朋友们&#xff0c;你们知道吗&#xff1f;自然语言处理领域最近取得了巨大的突破&#xff01;大型语言模型&#xff08;LLM&#xff09;的出现&#xff0c;简直就像打开了新世界的大门。它们不仅在语言理解、生成和翻译方面表现出色&#xff0c;还能涉足许多其他领域&…

网站创建的流程是什么

网站的创建过程包括几个主要的步骤&#xff0c;其中涉及到一系列的决策和实践操作。下面我将详细介绍网站创建的流程&#xff0c;帮助读者了解如何创建一个成功的网站。 第一步&#xff1a;确定网站目标和功能 在创建网站之前&#xff0c;你需要明确自己网站的目标和功能。是用…

【Proteus】51单片机对直流电机的控制

直流电机&#xff1a;输出或输入为直流电能的旋转电机。能实现直流电能和机械能互相转换的电机。把它作电动机运行时是直流电动机&#xff0c;电能转换为机械能&#xff1b;作发电机运行时是直流发电机&#xff0c;机 械能转换为电能。 直流电机的控制&#xff1a; 1、方向控制…

详细介绍医用PSA变压吸附制氧机设备的工艺特点

随着技术的不断进步&#xff0c;医用氧气作为一种重要的治疗资源&#xff0c;其供应方式也在不断地改进和升级。其中&#xff0c;医用PSA(Pressure Swing Adsorption&#xff0c;变压吸附)变压吸附制氧机设备因其高效、安全、稳定的特点&#xff0c;受到了广大机构的青睐。那么…

Biome 1.7 发布,支持从 ESLint 和 Prettier 迁移

近日&#xff0c;Biome v1.7 正式发布&#xff01;这个新版本提供了从 ESLint 和 Prettier 迁移的简单路径。它还引入了格式化程序和 linter 的实验性机器可读报告、新的 linter 规则和许多修复。 使用以下命令更新 Biome&#xff1a; npm install --save-dev --save-exact b…

DSPE-PEG-TPP 磷脂聚乙二醇-磷酸三苯酯 靶向线粒体纳米颗粒药物递送系统

DSPE-PEG-TPP 磷脂聚乙二醇-磷酸三苯酯 靶向线粒体纳米颗粒药物递送系统 【中文名称】磷脂-聚乙二醇-磷酸三苯酯 【英文名称】DSPE-PEG-TPP 【结 构】 【品 牌】碳水科技&#xff08;Tanshtech&#xff09; 【纯 度】95%以上 【保 存】-20℃ 【规 格】50mg,…

windows11 wsl2 ubuntu20.04安装vision mamba并进行测试

windows11 wsl2 ubuntu20.04安装vision mamba 安装流程使用cifar-100测试安装成功 安装流程 vision mamba安装了半天才跑通&#xff0c;记录一下流程在wsl上安装cuda wget https://developer.download.nvidia.cn/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05…

浅析ARM Contex-CM3内核架构

目录 概述 1. Cortex-M3类型MCU 1.1 MCU 架构 1.2 实时性系统概念 1.3 处理器命名法 1.4 MCU的一些知识 2. Cortex-M3 概览 2.1 Cortex-M3综述 2.2 寄存器组 2.3 操作模式和特权极别 2.4 内建的嵌套向量中断控制器 2.5 存储器映射 2.6 总线接口 2.7 存储器保护单元…