搜索+回溯问题(DFS\BFS详解)

文章目录

  • 题目
  • 思路
    • DFS
      • 思路
      • 代码
      • 复杂度分析
    • BFS
      • 思路
      • 代码
      • 复杂度分析


题目

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3

示例 2:

输入:m = 3, n = 1, k = 0
输出:1

提示:

1 <= n,m <= 100
0 <= k <= 20

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof


思路

依然是典型的搜索+回溯的问题。可以选择用DFS也可以选择用BFS。

先讲两种方法都需要解决的问题——求数位和。

int sums(int x){int sum = 0;while(x != 0) {sum += x % 10;x /= 10;}return sum;
}

这里引入一个数位和增量公式,是我看作者:jyd的题解时学到的:
在这里插入图片描述


DFS

思路

DFS 通过递归,先朝一个方向搜到底(本题中应为右、下两个方向之一),再回溯至上个节点,沿另一个方向搜索。

可行性剪枝: 在搜索中,遇到数位和超出给定值、此元素已访问,则应立即返回。

判定元素是否被访问通过建立一个m*n的bool数组,访问过的位置标记为true,未访问的位置标记为false。

  • 根据可行性剪枝确定终止条件:
  1. 下标越界
  2. 数位和超出给定值
  3. 元素已经被访问

此时返回0,搜索结果为不可达解。

  • 递推工作:
  1. 将当前节点标记为true,表示已经访问过。
  2. 从当前节点的 下、右 两个方向开启下层递归。
  • 回溯返回值:
    返回 1 + 下方搜索的可达解总数 + 右方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。

代码

class Solution {int rows;int cols;bool sumnumber(int i, int j, int k){int sum = 0;while(i != 0 || j != 0){if(i != 0){sum += i % 10;i /= 10;}if(j != 0){sum += j % 10;j /= 10;}}if(sum <= k){return true;}return false;}int dfs(int i, int j, vector<vector<bool>>& vb, int& k){bool flag = sumnumber(i, j, k);if(i >= rows || j >= cols || !flag || vb[i][j]){return 0;} vb[i][j] = true;return 1 + dfs(i + 1, j, vb, k) + dfs(i, j + 1, vb, k);}
public:int movingCount(int m, int n, int k) {vector<vector<bool>> vb(m, vector<bool>(n, 0));rows = m;cols = n;return dfs(0, 0, vb, k);}
};

复杂度分析

设矩阵行列数分别为 M, N

  • 时间复杂度 O(MN): 最差情况下,机器人遍历矩阵所有单元格,此时时间复杂度为 O(MN)。
  • 空间复杂度 O(MN): 最差情况下,vector<vector<bool>> vb 内存储矩阵所有单元格的索引,使用 O(MN)的额外空间。


BFS

思路

两者都是遍历矩阵,但区别在于:

  • DFS是向一个方向遍历到底,再回溯,不撞南墙不回头。
  • BFS是平推的方式向前搜索。

BFS通常通过队列来实现。

  • 初始化: 将初始点 (0, 0) 加入队列 queue ;
  • 迭代终止条件: queue 为空。代表已遍历完所有可达解。
  • 迭代过程:
  1. 单元格出队: 将队首单元格的 索引、数位和 弹出,作为当前搜索单元格。
  2. 判断是否跳过:
    行列索引越界
    数位和超出目标值 k
    当前元素已访问过
    时,执行 continue 。
  3. 标记当前单元格 : 将单元格索引 (i, j) 存入 辅助数组 中,代表此单元格 已被访问过
  4. 单元格入队: 将当前元素的 下方、右方 单元格的 索引、数位和 加入 queue
  • 返回值: 辅助数组 的长度即为可达解的数量。

代码

class Solution {bool sumnumber(int i, int j, int k){int sum = 0;while(i != 0 || j != 0){if(i != 0){sum += i % 10;i /= 10;}if(j != 0){sum += j % 10;j /= 10;}}if(sum <= k){return true;}return false;}
public:int movingCount(int m, int n, int k) {queue<pair<int,int>> que; // 临时存储可达解,为空则表示已经遍历完可达解vector<vector<bool>> vb(m, vector<bool>(n, 0)); // 记录访问过的节点vector<int> dx = {0,1}; // 向右、下的方向数组vector<int> dy = {1,0}; // 向右、下的方向数组int res = 0; // 可达解总数que.push(make_pair(0, 0));// 等价于  //que.push({ 0, 0 });while(!que.empty()){pair<int,int> x = que.front();que.pop();int i = x.first, j = x.second;bool flag = sumnumber(i, j, k);if(i >= m || j >= n || vb[i][j] || !flag) continue;vb[i][j] = true;res++;for(int a = 0; a < 2; a++){ // 当前单元格可达int tx = i + dx[a]; int ty = j + dy[a];que.push(make_pair(tx, ty));// 将右、下方向的单元格入队}}return res;}
};

复杂度分析

同DFS

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快速幂实现pow函数(从二分和二进制两种角度理解快速幂)

文章目录迭代实现快速幂思路int的取值范围快速幂从二进制的角度来理解从二分法的角度来理解代码复杂度分析进阶——超级次方思路倒序快速幂正序快速幂代码复杂度分析迭代实现快速幂 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff0…

n位数的全排列(需要考虑大数的情况)

文章目录题目思路代码题目 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n 1 输出: [1,2,3,4,5,6,7,8,9] 说明&#xff1a; 用返回一个整数列表来代替打印 n 为正整数 …

正则表达式匹配(动规)

文章目录题目思路转移方程特征再探 i 和 j代码题目 请实现一个函数用来匹配包含 . 和 * 的正则表达式。模式中的字符 . 表示任意一个字符&#xff0c;而 * 表示它前面的字符可以出现任意次&#xff08;含0次&#xff09;。在本题中&#xff0c;匹配是指字符串的所有字符匹配整…

在循环递增一次的数组中插入元素

文章目录题目思路如何建立左右区间&#xff1f;如何查找最高点&#xff1f;那我们怎么判断 num 到底处于什么样的位置呢&#xff1f;如何确定插入位置&#xff1f;插入元素代码题目 给一个只循环递增一次的数组 res&#xff0c;res 满足首元素大于等于尾元素&#xff0c;形如&…

表示数值的字符串(有限状态自动机与搜索)

文章目录题目思路一代码一思路二代码二题目 思路一 考察有限状态自动机&#xff08;参考jyd&#xff09;&#xff1a; 字符类型&#xff1a; 空格 「 」、数字「 0—9 」 、正负号 「 」 、小数点 「 . 」 、幂符号 「 eE 」 。 状态定义&#xff1a; 按照字符串从左到右的…

树的子结构

文章目录题目深搜深搜代码广搜广搜代码题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A: 给定的树 B&#xff1a; 返回 true&#xff0c;因为…

写题过程中碰见的小问题

文章目录和--vector二维vector的初始化数组中最大的数max_element()数组所有元素之和accumulate()vector数组去重对pair类型的vector排序对元素都为正整数的vector利用sort默认的升序排列进行降序排列一维数组转二维数组size_t和int如何不用临时变量交换两个数?将类函数的形参…

LeetCode——二叉树序列化与反序列化

文章目录题目思路问题一问题二代码实现题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 设计一个算法来实现二叉树的序列化与反序列化。不限定序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序…

jsp中生成的验证码和存在session里面的验证码不一致的处理

今天在调试项目的时候发现&#xff0c;在提交表单的时候的验证码有问题&#xff0c;问题是这样的&#xff1a;就是通过debug模式查看得知&#xff1a;jsp页面生成的验证码和表单输入的页面输入的一样&#xff0c;但是到后台执行的时候&#xff0c;你会发现他们是不一样的&#…

求1~n这n个整数十进制表示中1出现的次数

文章目录题目思路代码复杂度分析题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;那么1&#xff5e;12这些整数中包含1 的数字有1、10、11和12。可得1一共出现了5次。 思路 将个位、十位……每位…

求数字序列中的第n位对应的数字

文章目录题目思路代码复杂度分析致谢题目 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中&#xff0c;第5位&#xff08;从下标0开始计数&#xff09;是5&#xff0c;第13位是1&#xff0c;第19位是4&#xff0c;等等。 请写一个函数&#xff0c…

一学就废的归并排序

文章目录其他与排序有关的文章原理代码实现复杂度分析其他与排序有关的文章 一学就废的三种简单排序【冒泡、插入、选择】 原理 归并排序&#xff08;Merge sort&#xff09;&#xff1a; 归并排序对元素 递归地 进行 逐层折半分组&#xff0c;然后从最小分组开始&#xff0c…

树状数组的相关知识 及 求逆序对的运用

文章目录树状数组概念前缀和和区间和树状数组原理区间和——单点更新前缀和——区间查询完整代码离散化sort函数unique函数去重erase函数仅保留不重复元素通过树状数组求逆序对树状数组概念 树状数组又名二叉索引树&#xff0c;其查询与插入的复杂度都为 O(logN)&#xff0c;其…

二叉搜索树相关知识及应用操作

文章目录概念查找二叉搜索树的第k大节点概念 二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又名&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;——它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若它的…

二叉树相关知识及求深度的代码实现

文章目录树二叉树满二叉树和完全二叉树二叉树的性质代码实现求二叉树的深度树 树是一种非线性的数据结构&#xff0c;它是由n个有限结点组成一个具有层次关系的集合。 树的相关名词&#xff1a; 根节点&#xff1a;没有前驱结点的结点。父节点&#xff0c;子节点&#xff1a…

大端小端存储模式详解及判断方法

文章目录大小端模式的概念两种模式出现原因两种模式的优劣大小端的应用情景判断机器的字节序大小端模式的概念 当我们查看数据在内存中的存储情况时&#xff0c;我们经常会发现一个很奇怪的现象&#xff0c;什么现象呢&#xff1f; int main() {int i 12;return 0; }数据在内…

Linux 内存管理 | 物理内存、内存碎片、伙伴系统、SLAB分配器

文章目录物理内存物理内存分配外部碎片内部碎片伙伴系统(buddy system)slab分配器物理内存 在Linux中&#xff0c;内核将物理内存划分为三个区域。 在解释DMA内存区域之前解释一下什么是DMA&#xff1a; DMA&#xff08;直接存储器访问&#xff09; 使用物理地址访问内存&am…

Linux 内存管理 | 虚拟内存管理:虚拟内存空间、虚拟内存分配

文章目录虚拟地址空间用户空间内核空间用户空间内存分配malloc内核空间内存分配kmallocvmalloc虚拟地址空间 在早期的计算机中&#xff0c;程序是直接运行在物理内存上的&#xff0c;而直接使用物理内存&#xff0c;通常都会面临以下几种问题&#xff1a; 内存缺乏访问控制&a…

Linux | 编译原理、gcc的命令参数、自动化构建工具 make/Makefile

文章目录编译原理预处理编译汇编链接gcc的常用命令参数make 和 Makefile 的概念make的运行通配符自动化变量伪目标.PHONE:【命令】编译原理 在解释 makefile 前&#xff0c;首先解释一下 .c 文件变成 .exe 文件要经过的四个步骤——预处理、编译、汇编和链接&#xff08;参考来…

Linux | 进程概念、进程状态(僵尸进程、孤儿进程、守护进程)、进程地址空间

文章目录进程和程序操作系统如何控制和调度程序进程控制块–PCB子进程进程状态僵尸进程孤儿进程守护进程&#xff08;精灵进程&#xff09;进程地址空间引言页表进程和程序 程序&#xff1a; 一系列有序的指令集合&#xff08;就是我们写的代码&#xff09;。进程&#xff1a;…