递归、搜索与回溯算法:综合练习

例题一


解法:
算法思路:
⾸先,我们在第⼀⾏放置第⼀个皇后,然后遍历棋盘的第⼆⾏,在可⾏的位置放置第⼆个皇后,然后再遍历第三⾏,在可⾏的位置放置第三个皇后,以此类推,直到放置了 n 个皇后为⽌。
我们需要⽤⼀个数组来记录每⼀⾏放置的皇后的列数。在每⼀⾏中,我们尝试放置⼀个皇后,并检查是否会和前⾯已经放置的皇后冲突。如果没有冲突,我们就继续递归地放置下⼀⾏的皇后,直到所有的皇后都放置完毕,然后把这个⽅案记录下来。
在检查皇后是否冲突时,我们可以⽤⼀个数组来记录每⼀列是否已经放置了皇后,并检查当前要放置的皇后是否会和已经放置的皇后冲突。对于对⻆线,我们可以⽤两个数组来记录从左上⻆到右下⻆的每⼀条对⻆线上是否已经放置了皇后,以及从右上⻆到左下⻆的每⼀条对⻆线上是否已经放置了皇后。
• 对于对⻆线是否冲突的判断可以通过以下流程解决:

  1. 从左上到右下:相同对⻆线的⾏列之差相同;
  2. 从右上到左下:相同对⻆线的⾏列之和相同。
    因此,我们需要创建⽤于存储解决⽅案的⼆维字符串数组 ret ,⽤于存储每个皇后的位置的
    ⼀维整数数组 path ,以及⽤于记录每⼀列和对⻆线上是否已经有皇后的布尔型数组
    checkcol 、 checkdiag1 和 check diag2 。
    全局变量:
    vector<vector<string>> ret;
    vector<string> path;
    int _n;
    vector<bool> checkcol;
    vector<bool> checkdiag1, checkdiag2;
    递归函数设计:void dfs(int pos)
    参数:row(当前需要处理的⾏数);
    返回值:⽆;
    函数作⽤:在当前⾏放⼊⼀个不发⽣冲突的皇后,查找所有可⾏的⽅案使得放置 n 个皇后后不发⽣冲突。
    递归函数流程如下:
  3. 结束条件:如果 row 等于 n ,则表⽰已经找到⼀组解决⽅案,此时将每个皇后的位置存储到字
    符串数组 path 中,并将 path 存储到 ret 数组中,然后返回;
  4. 枚举当前⾏的每⼀列,判断该列、两个对⻆线上是否已经有皇后:
    a. 如果有皇后,则继续枚举下⼀列;
    b. 否则,在该位置放置皇后,并将 checkcol 、 checkdiag1 和 check diag2 对应的位置设为 true ,表⽰该列和对⻆线上已经有皇后:
    i. 递归调⽤ dfs 函数,搜索下⼀⾏的皇后位置。如果该⽅案递归结束,则在回溯时需要将 checkcol 、 checkdiag1 和 check diag2 对应的位置设为 false ,然后继续枚举下⼀列;

例题二


解法:
算法思路:
创建三个数组标记⾏、列以及 3*3 ⼩⽅格中是否出现 1~9 之间的数字即可。

例题三


解法:
算法思路:
为了存储每个位置的元素,我们需要定义⼀个⼆维数组。⾸先,我们记录所有已知的数据,然后遍历所有需要处理的位置,并遍历数字 1~9。对于每个位置,我们检查该数字是否可以存放在该位置,同时检查⾏、列和九宫格是否唯⼀。
我们可以使⽤⼀个⼆维数组来记录每个数字在每⼀⾏中是否出现,⼀个⼆维数组来记录每个数字在每⼀列中是否出现。对于九宫格,我们可以以⾏和列除以 3 得到的商作为九宫格的坐标,并使⽤⼀个三维数组来记录每个数字在每⼀个九宫格中是否出现。在检查是否存在冲突时,只需检查⾏、列和九宫格⾥对应的数字是否已被标记。如果数字⾄少有⼀个位置(⾏、列、九宫格)被标记,则存在冲突,因此不能在该位置放置当前数字。
• 特别地,在本题中,我们需要直接修改给出的数组,因此在找到⼀种可⾏的⽅法时,应该停⽌递归,以防⽌正确的⽅法被覆盖。
初始化定义:

  1. 定义⾏、列、九宫格标记数组以及找到可⾏⽅法的标记变量,将它们初始化为 false。
  2. 定义⼀个数组来存储每个需要处理的位置。
  3. 将题⽬给出的所有元素的⾏、列以及九宫格坐标标记为 true。
  4. 将所有需要处理的位置存⼊数组。
    递归函数设计:void dfs(vector<vector<char>>& board)
    参数:无;
    返回值:⽆;
    函数作⽤:在当前坐标填⼊合适数字,查找数独答案。
    递归流程如下:
  5. 结束条件:已经处理完所有需要处理的元素。如果找到了可⾏的解决⽅案,则将标记变量更新为
    true 并返回。
  6. 获取当前需要处理的元素的⾏列值。
  7. 遍历数字 1~9。如果当前数字可以填⼊当前位置,并且标记变量未被赋值为 true,则将当前位置的⾏、列以及九宫格坐标标记为 true,将当前数字赋值给 board 数组中的相应位置元素,然后对下⼀个位置进⾏递归。
  8. 递归结束时,撤回标记。

例题四


解法:
算法思路:
我们需要假设每个位置的元素作为第⼀个字⺟,然后向相邻的四个⽅向进⾏递归,并且不能出现重复使⽤同⼀个位置的元素。通过深度优先搜索的⽅式,不断地枚举相邻元素作为下⼀个字⺟出现的可能性,并在递归结束时回溯,直到枚举完所有可能性,得到正确的结果。
递归函数设计:bool dfs(vector<vector<char>>& board,int i,int j,string& word,int pos)
参数:i(当前需要进⾏处理的元素横坐标),j(当前需要进⾏处理的元素横坐标),pos(当前已
经处理的元素个数),word(字符串);
函数作⽤:判断当前坐标的元素作为字符串中下标 pos 的元素出现时,向四个⽅向传递,查找是否存在路径结果与字符串相同。
递归函数流程:

  1. 遍历每个位置,标记当前位置并将当前位置的字⺟作为⾸字⺟进⾏递归,并且在回溯时撤回记。
  2. 在每个递归的状态中,我们维护⼀个步数 pos,表⽰当前已经处理了⼏个字⺟。
    ◦ 若当前 pos 的值与字符串⻓度相等,表⽰存在⼀种路径使得 word 成⽴,返回 true。
  3. 对当前位置的上下左右四个相邻位置进⾏递归,若递归结果为 true,则返回 true。
  4. 若相邻的四个位置的递归结果都为 false,则返回 false。
    • 特别地,如果使⽤将当前遍历到的字符赋值为空格,并在回溯时恢复为原来的字⺟的⽅法,则在递归时不会重复遍历当前元素,可达到不使⽤标记数组的⽬的。

例题五


解法:
算法思路:
枚举矩阵中所有的位置当成起点,来⼀次深度优先遍历,统计出所有情况下能收集到的⻩⾦数的最⼤值即可。

例题六


解法:
算法思路:
对于四个⽅向,我们可以定义两个一维数组 dx,dy ,⼤⼩为 4 ,每⼀维存储四个⽅向的坐标偏移量 (详⻅代码)。题⽬要求到达⽬标位置时所有⽆障碍⽅格都存在路径中,我们可以定义⼀个变量记录 num 当前状态中剩余的未⾛过的⽆障碍⽅格个数,则当我们⾛到⽬标地点时只需要判断 num 是否为 0 即可。在移动时需要判断是否越界。
全局变量:
vector<vector<bool>> visited;
int m, n;
int ret,cou=0;
int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
递归函数设计:void dfs(vector<vector<int>>& grid,int i,int j,int path)
参数:i,j(当前需要处理元素的坐标),path(当前行走的步数);
返回值:⽆;
函数作⽤:判断当前位置的四个⽅向是否可以添加⾄当前状态,查找在满⾜条件下从起始⽅格到结束⽅格的不同路径的数⽬。
递归流程如下:

  1. 递归结束条件:当前位置的元素值为 2,若此时已经⾛的位置数量 path 的值为 cou,则 ret 的值加⼀;
  2. 遍历四个⽅向,若移动后未越界,⽆障碍并且未被标记,则标记当前位置,并递归移动后的位置,在回溯时撤销标记操作。
相关推荐
是小胡嘛25 分钟前
数据结构之旅:红黑树如何驱动 Set 和 Map
数据结构·算法
m0_7482550228 分钟前
前端常用算法集合
前端·算法
呆呆的猫1 小时前
【LeetCode】227、基本计算器 II
算法·leetcode·职场和发展
Tisfy1 小时前
LeetCode 1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解
算法·leetcode·优先队列·贪心·
余额不足121381 小时前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
火星机器人life3 小时前
基于ceres优化的3d激光雷达开源算法
算法·3d
虽千万人 吾往矣4 小时前
golang LeetCode 热题 100(动态规划)-更新中
算法·leetcode·动态规划
arnold664 小时前
华为OD E卷(100分)34-转盘寿司
算法·华为od
ZZTC5 小时前
Floyd算法及其扩展应用
算法
lshzdq5 小时前
【机器人】机械臂轨迹和转矩控制对比
人工智能·算法·机器人