【三十九】【算法分析与设计】综合练习(5),79. 单词搜索,1219. 黄金矿工,980. 不同路径 III

news2024/11/27 22:42:58

79. 单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED" 输出:true

示例 2:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE" 输出:true

示例 3:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB" 输出:false

提示:

  • m == board.length

  • n = board[i].length

  • 1 <= m, n <= 6

  • 1 <= word.length <= 15

  • boardword 仅由大小写英文字母组成

进阶:你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?

宏观地看待递归。递归函数,自己调用自己,同一个函数需要表示递归图中任何一个节点。

因此我们需要一些变量与递归函数进行绑定,这些变量帮助我们知道现在的递归函数是在递归图的哪一个节点。

其次,我们还需要能够知道如何从当前的递归节点到达孩子递归节点,这一个过程也需要一些变量的帮助。

因此我们有两个需要做的事情,第一件事是知道当前递归函数代表递归图的哪一个节点。

第二件事是知道如何从当前递归图节点到达孩子递归图节点。

bool dfs(vector<vector<char>>& board, int i, int j, int pos) {

递归函数是这样定义的,它表示当前在递归图的位置(i,j)对应的位置,它如何找到孩子递归图节点,通过pos变量,pos表示word中下一个查找的值,也就是孩子节点对应的值。

对于递归图特定节点,有四个可能的子孩子位置,分别是(i,j)位置的左边,右边,上边,下边。

左边是(i,j-1),右边是(i,j+1),上边是(i-1,j),下边是(i+1,j)。

此时思考如何剪枝,走过的路我们不走,因此需要一个visit数组,用来划分集合,一个集合是走过的路,一个集合是没有走过的路。所以只需要两个不同的值对应即可。可以思考到bool类型。

如果使用int类型,可以对应多个集合,不同的int类型值对应一个集合,例如1对应一个集合,2对应一个集合,等等以此类推。

思考递归出口,如果当前递归图节点,pos==word.size(),说明当前节点已经是最后一个单词的字母。此时直接返回就可以了。

 
class Solution {
public:
    bool visit[7][7];
    int row, col;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    string word;
    bool exist(vector<vector<char>>& board, string _word) {
        word = _word;
        row = board.size();
        col = board[0].size();
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (board[i][j] == word[0]) {
                    visit[i][j] = true;
                    if (dfs(board, i, j, 1))
                        return true;
                    visit[i][j] = false;
                }
            }
        }
        return false;
    }

    bool dfs(vector<vector<char>>& board, int i, int j, int pos) {
        if (pos == word.size()) {
            return true;
        }

        for (int k = 0; k < 4; k++) {
            int x = i + dx[k];
            int y = j + dy[k];

            if (x >= 0 && x < row && y >= 0 && y < col && !visit[x][y] &&
                board[x][y] == word[pos]) {
                visit[x][y] = true;
                if (dfs(board, x, y, pos + 1))
                    return true;
                visit[x][y] = false;
            }
        }
        return false;
    }
};

定义全局变量,就不需要给递归函数进行传参。

bool visit[7][7];

定义bool类型的visit数组,进行集合的划分,visit[i][j]对应(i,j)位置,true表示使用过,false表示没有使用过。

int row, col;

定义全局变量row,col分别表示行与列。

int dx[4] = {0, 0, 1, -1}; int dy[4] = {1, -1, 0, 0};

如何快速得到(i,j)位置上下左右四个方位的坐标,利用向量法。

可以知道,这四个位置分别是(i+1,j),(i-1,j),(i,j+1),(i,j-1)。

可以写成(i,j)+(1,0),(i,j)+(-1,0),(i,j)+(0,1),(i,j)+(0,-1)。

只需要表示出增量即可。

dx表示x方向的增量,dy表示y方向的增量。

dx[k],dy[k]共同表示某一个增量组合。

因此dx dy的形式,其中一个增量数组0,0,1,-1。

另一个增量数组在1,-1出现的位置只出现0。另外两个位置出现1,-1。

string word;

小技巧,将函数中的变量变成全局变量,修改名字,在名字前面添加下划线_,然后再全局变量创建一个原名,在函数中赋值过去即可。 bool exist(vector<vector<char>>& board, string _word) { word = _word; row = board.size(); col = board[0].size();

对全局变量的计算,初始化。

for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { if (board[i][j] == word[0]) { visit[i][j] = true; if (dfs(board, i, j, 1)) return true; visit[i][j] = false; } } }

遍历递归图中最开始的位置,如果找到word中第一个字符,此时这个位置就是最开始的位置。

注意维护变量,visit。因为visit是全局变量,所以需要手动回溯。

为什么需要回溯?因为这些变量共同表示递归图中某一个位置的节点,当前是这个节点,这些变量就必须维护对应的值。

但是部分节点变量在递归函数中作为形参,此时系统会自动帮我们进行回溯操作。

小技巧:如果是int char等类型,空间小的数据类型,可以放到递归函数中作为形参。

如果是vector空间大的数据类型,放到全局变量中,而不是放到递归韩式作为形参。

因为作为形参每一次都需要重新开辟空间,赋值,如果空间大的这种消耗比较大。

if (dfs(board, i, j, 1)) return true;

注意这条语句,这种用法,是递归寻找某一个特定的值的时候使用,当找到之后,就不需要再递归下去了。

如果找到了,就返回true,如果递归图中子节点找到了,当前节点就不需要再递归其他可能性了,直接返回true。

可以理解为,定义递归函数bool表示当前递归节点树,中是否能够找到。true表示找到了。

如果遍历完,没有返回true,说明所有递归图节点树都没有找到,返回false。 return false; bool dfs(vector<vector<char>>& board, int i, int j, int pos) { if (pos == word.size()) { return true; }

递归的出口,pos表示递归图子节点的可能性。pos==word.size()表示当前节点就是最后一个字母,此时找到了序列,直接返回true。

for (int k = 0; k < 4; k++) { int x = i + dx[k]; int y = j + dy[k]; if (x >= 0 && x < row && y >= 0 && y < col && !visit[x][y] && board[x][y] == word[pos]) { visit[x][y] = true; if (dfs(board, x, y, pos + 1)) return true;

如果递归图子树找到了,不需要继续递归下去了,直接返回true。

visit[x][y] = false; } } return false;

如果当前节点的所有子树都没有找到,说明当前节点也找不到,返回false。

1219. 黄金矿工

你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 m * n 的网格 grid 进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是 0

为了使收益最大化,矿工需要按以下规则来开采黄金:

  • 每当矿工进入一个单元,就会收集该单元格中的所有黄金。

  • 矿工每次可以从当前位置向上下左右四个方向走。

  • 每个单元格只能被开采(进入)一次。

  • 不得开采(进入)黄金数目为 0 的单元格。

  • 矿工可以从网格中 任意一个 有黄金的单元格出发或者是停止。

示例 1:

输入:grid = [[0,6,0],[5,8,7],[0,9,0]] 输出:24 解释: [[0,6,0], [5,8,7], [0,9,0]] 一种收集最多黄金的路线是:9 -> 8 -> 7。

示例 2:

输入:grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]] 输出:28 解释: [[1,0,7], [2,0,6], [3,4,5], [0,3,0], [9,0,20]] 一种收集最多黄金的路线是:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7。

提示:

  • 1 <= grid.length, grid[i].length <= 15

  • 0 <= grid[i][j] <= 100

  • 最多 25 个单元格中有黄金。

 
class Solution {
public:
    int row, col;
    bool visit[16][16];
    int ret;
    int getMaximumGold(vector<vector<int>>& grid) {
        row = grid.size(), col = grid[0].size();
        for (int i = 0; i < row; i++)
            for (int j = 0; j < col; j++) {
                if (grid[i][j] != 0) {
                    visit[i][j] = true;
                    dfs(grid, i, j, grid[i][j]);
                    visit[i][j] = false;
                }
            }
        return ret;
    }
    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) {
        ret = max(ret, path);

        for (int k = 0; k < 4; k++) {
            int x = i + dx[k], y = j + dy[k];
            if (x >= 0 && x < row && y >= 0 && y < col && !visit[x][y] &&
                grid[x][y] != 0) {
                visit[x][y] = true;
                dfs(grid, x, y, path + grid[x][y]);
                visit[x][y] = false;
            }
        }
    }
};

定义全局变量,这样就不需要给递归函数传参数了。

int row, col;

row表示行数,col表示列数。

bool visit[16][16];

划分集合,用来表示(i,j)位置是否被使用,true被使用,false没有被使用。

int ret;

记录结果。

int getMaximumGold(vector<vector<int>>& grid) {

row = grid.size(), col = grid[0].size();

给row和col初始化。

for (int i = 0; i < row; i++)

for (int j = 0; j < col; j++) {

两层for循环遍历最开始递归图的节点。

if (grid[i][j] != 0) {

visit[i][j] = true;

dfs(grid, i, j, grid[i][j]);

这里没有使用bool,返回true的用法,是因为我需要递归所有情况,找到一种情况之后还需要继续递归。

visit[i][j] = false;

手动回溯。

int dx[4] = {0, 0, -1, 1}, dy[4] = {1, -1, 0, 0};

定义增量数组,用来表示(i,j)的四个方位。

void dfs(vector<vector<int>>& grid, int i, int j, int path) {

ret = max(ret, path);

path表示递归图当前节点的有效路径。ret记录所有情况下的最大值。

for (int k = 0; k < 4; k++) {

int x = i + dx[k], y = j + dy[k];

if (x >= 0 && x < row && y >= 0 && y < col && !visit[x][y] &&

grid[x][y] != 0) {

如果x,y位置没有越界,并且没有没使用过,并且值不为0,此时是合法位置。

visit[x][y] = true;

dfs(grid, x, y, path + grid[x][y]);

visit[x][y] = false;

980. 不同路径 III

在二维网格 grid 上,有 4 种类型的方格:

  • 1 表示起始方格。且只有一个起始方格。

  • 2 表示结束方格,且只有一个结束方格。

  • 0 表示我们可以走过的空方格。

  • -1 表示我们无法跨越的障碍。

返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格

示例 1:

  1. 输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] 输出:2 解释:我们有以下两条路径: (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2) (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)

示例 2:

  1. 输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]] 输出:4 解释:我们有以下四条路径: (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3) (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3) (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)

示例 3:

输入:[[0,1],[2,0]] 输出:0 解释: 没有一条路能完全穿过每一个空的方格一次。 请注意,起始和结束方格可以位于网格中的任意位置。

提示:

  • 1 <= grid.length * grid[0].length <= 20

 
class Solution {
public:
    int row, col; // 定义行数和列数
    int visit[21][21]; // 访问标记数组,记录网格中的位置是否被访问过
    int step; // 步数计数器,记录从起点到终点需要经过的格子数量
    int ret; // 结果计数器,记录所有满足条件的路径数量

    int uniquePathsIII(vector<vector<int>>& grid) {
        row = grid.size(), col = grid[0].size(); // 初始化行数和列数
        int bx, by; // 起点的坐标
        for (int i = 0; i < row; i++)
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == 0)
                    step++; // 如果格子为0,说明是空格,需要经过,步数加1
                else if (grid[i][j] == 1)
                    bx = i, by = j; // 如果格子为1,说明是起点,记录起点坐标
            }

        step += 2; // 加上起点和终点的格子
        visit[bx][by] = true; // 标记起点已访问
        dfs(grid, bx, by, 1); // 从起点开始进行深度优先搜索
        return ret; // 返回所有满足条件的路径数量
    }
    int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1}; // 方向数组,用于实现上下左右移动

    void dfs(vector<vector<int>>& grid, int i, int j, int count) {
        if (grid[i][j] == 2) { // 如果当前格子是终点
            if (step == count)
                ret++; // 如果当前路径长度等于所需步数,结果加1
            return;
        }
        for (int k = 0; k < 4; k++) { // 遍历四个方向
            int x = i + dx[k], y = j + dy[k]; // 计算下一个格子的坐标
            if (x >= 0 && x < row && y >= 0 && y < col && !visit[x][y] &&
                grid[x][y] != -1) { // 确保下一个格子在网格内,未被访问过,且不是障碍物
                visit[x][y] = true; // 标记为已访问
                dfs(grid, x, y, count + 1); // 递归搜索下一个格子
                visit[x][y] = false; // 回溯,取消标记
            }
        }
    }
};

 

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

二豆写作能用吗 #笔记#笔记

二豆写作是一款非常好用、靠谱、方便的论文写作工具&#xff0c;它能帮助用户快速完成论文写作&#xff0c;并且具有查重降重的功能。那么&#xff0c;二豆写作到底能不能用呢&#xff1f;答案是肯定的&#xff0c;二豆写作绝对是值得推荐的。 首先&#xff0c;二豆写作提供了丰…

不定长顺序表

一.不定长顺序表的结构: typedef struct DSQList{ int* elem;//动态内存的地址 int length;//有效数据的个数 int listsize;//总容量 }DSQList,*DPSQList; 很明显,为了能实现扩容(否则如何实现再次判满呢?),我们必须要在定长顺序表的基础上增加一个总容量;结构示意图如下: 二…

基于ros的相机内参标定过程

基于ros的相机内参标定过程 1. 安装还对应相机的驱动2. 启动相机节点发布主题3. 下载camera_calibartion4. 将红框的文件夹复制在自己的工作空间里边&#xff0c;编译5. 标定完成以后&#xff0c;生成内参参数文件camera.yaml。将文件放在对应的路径下&#xff0c;修改config文…

(二)ffmpeg 拉流推流示例

一、搭建流媒体服务器 在这里&#xff0c;选用的流媒体服务器是mediamtx。 下载地址&#xff1a;https://github.com/bluenviron/mediamtx/releases/tag/v1.6.0 系统不同选择的压缩包不同&#xff0c;我用的是ubuntu系统。 下载下来之后进行解压&#xff0c;可以看到里面有三…

【随笔】Git 高级篇 -- 最近标签距离查询 git describe(二十一)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

Android 属性动画及自定义3D旋转动画

Android 动画框架 其中包括&#xff0c;帧动画、视图动画&#xff08;补间动画&#xff09;、属性动画。 在Android3.0之前&#xff0c;视图动画一家独大&#xff0c;之后属性动画框架被推出。属性动画框架&#xff0c;基本可以实现所有的视图动画效果。 视图动画的效率较高…

常见性能测试工具对比

在性能测试工作中&#xff0c;我们常常会遇到好几个工具&#xff0c;但是每一个工具都有自己的优势&#xff0c;一时间不知道怎么选择。 今天我们就将性能测试常用的工具进行对比&#xff0c;这样大家在选择工具的时候心里就有底啦&#xff01; 阿里云PTS 性能测试PTS&#xff…

25. 文档测试

作为下午题出现的几率很低&#xff1b; 主要议题&#xff1a; 1.软件文档分类 2.用户文档的内容 用户文档测试的作用&#xff1a; 3.用户文档测试需要注意的问题 4.用户文档测试的要点 5.用户手册测试 6.在线帮助测试

安卓的认证测试

1 CTS CTS 是 Android 兼容性测试套件&#xff0c;用于验证设备是否符合 Android 平台的兼容性标准。它包含一系列测试用例&#xff0c;涵盖了设备的各个方面&#xff0c;如硬件功能、软件功能、API 的正确实现等。通过 CTS 测试&#xff0c;设备厂商可以确保其设备符合 Andro…

工单管理系统设计方案,工单系统的流程

工单管理系统是一种用于管理和跟踪工作流程的软件系统。它可以帮助企业和组织更好地分配任务、优化工作流程、提高生产效率和客户满意度。下面是一个基本的工单管理系统设计方案&#xff1a;需求分析  在设计工单管理系统之前&#xff0c;需要进行需求分析&#xff0c;确定系…

C语言 | Leetcode C语言题解之第17题电话号码的字母组合

题目&#xff1a; 题解&#xff1a; char phoneMap[11][5] {"\0", "\0", "abc\0", "def\0", "ghi\0", "jkl\0", "mno\0", "pqrs\0", "tuv\0", "wxyz\0"};char* digits…

【论文解读】大模型事实性调查(下)

http://t.csdnimg.cn/4md5U 上期我们分享了《大模型事实性调查》论文解读的前半部分&#xff0c;这一期为大家带来后面的内容&#xff0c;欢迎阅读交流。 四、事实性分析 在前面的第3节中&#xff0c;论文提供了与评估事实性相关的定量统计数据。在本节中&#xff0c;论文将更…

841. 钥匙和房间

841. 钥匙和房间 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;_841钥匙和房间_dfs_841钥匙和房间_bfs 错误经验吸取 原题链接&#xff1a; 841. 钥匙和房间 https://leetcode.cn/problems/keys-and-rooms/description/ 完成情况&…

Vue.js组件精讲 基础:Vue.js组件的三个API:prop、event、slot

如果您已经对 Vue.js 组件的基础用法了如指掌&#xff0c;可以跳过本小节&#xff0c;不过当做复习稍读一下也无妨。 组件的构成 一个再复杂的组件&#xff0c;都是由三部分组成的&#xff1a;prop、event、slot&#xff0c;它们构成了 Vue.js 组件的 API。如果你开发的是一个…

clickhouse深入浅出

基础知识原理 极致压缩率 极速查询性能 列式数据库管理 &#xff0c;读请求多 大批次更新或无更新 读很多但用很少 大量的列 列的值小数值/短字符串 一致性要求低 DBMS&#xff1a;动态创建/修改/删除库 表 视图&#xff0c;动态查/增/修/删&#xff0c;用户粒度设库…

llama2.c与chinese-baby-llama2语言模型本地部署推理

文章目录 简介Github文档克隆源码英文模型编译运行中文模型&#xff08;280M&#xff09;main函数 简介 llama2.c是一个极简的Llama 2 LLM全栈工具&#xff0c;使用一个简单的 700 行 C 文件 ( run.c ) 对其进行推理。llama2.c涉及LLM微调、模型构建、推理端末部署&#xff08…

15.2024

全排列---邻里交换法 代码&#xff1a; public class 第十五题 {static int count;static int a[]{1,2,3,4,5,6,7,8,9};public static void main(String[] args) {f(a,0);System.out.println(count/6);}public static void f(int a[],int step){if(stepa.length-1){if (a[0]a…

大语言模型开源数据集

本文目标&#xff1a;汇聚目前大语言模型预训练、微调、RM/RL、评测等全流程所需的常见数据集&#xff0c;方便大家使用&#xff0c;本文持续更新。文章篇幅较长&#xff0c;建议收藏后使用。 一、按语料类型分类 1、维基百科类 No.1 Identifying Machine-Paraphrased Plagia…

Matlab:任意的三维Cubic空间中生成大小不一样的小球,并画出截面

生成小球和大球的代码块 clear all clc close all % entorid3D rand(10,3, 0.1,0.9);% for c11 0.05:0.3:0.95 % for c12 0.05:0.3:0.95 % for c13 0.05:0.3:0.95 % [x1,y1,z1] ellipsoid(c11, c12, c13, 0.05, 0.05, 0.05,100); % …

Vue+el-table 修改表格 单元格横线边框颜色及表格空数据时边框颜色

需求 目前 找到对应的css样式进行修改 修改后 css样式 >>>.el-table th.el-table__cell.is-leaf {border-bottom: 1px solid #444B5F !important;}第二个问题 修改 表格空数据时&#xff0c;边框颜色 css样式 >>>.el-table::before {background-color: tra…