递归 与 dfs 综合练习(四)

目录

一、单词搜索

1.题目链接:79. 单词搜索

2.题目描述:

3.解法

🌴算法思路:

🌴算法代码:

一、黄金矿工

1.题目链接:1219. 黄金矿工

2.题目描述:

3.解法

🌴算法思路:

🌴算法代码:

一、不同路径 III

1.题目链接:980. 不同路径 III

2.题目描述:

3.解法

🌴算法思路:

🌴算法代码:


一、单词搜索

1.题目链接:79. 单词搜索

2.题目描述:

给定一个 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
  • board 和 word 仅由大小写英文字母组成

3.解法

🌴算法思路:

        我们需要假设每个位置的元素作为第⼀个字母,然后向相邻的四个方向进行递归,并且不能出现重复使用同⼀个位置的元素。通过深度优先搜索的方式,不断地枚举相邻元素作为下⼀个字母出现的可能性,并在递归结束时回溯,直到枚举完所有可能性,得到正确的结果。


递归函数设计:bool dfs(int x, int y, int step, vector<vector<char>>& board, string word, vector<vector<bool>>& vis, int &n, int &m, int &len)

  • 参数:x(当前需要进行处理的元素横坐标),y(当前需要进行处理的元素横坐标),step(当前已经处理的元素个数),word(当前的字符串状态);
  • 返回值:当前坐标元素作为字符串中下标 step 的元素出现是否可以找到成立的字符串。
  • 函数作用:判断当前坐标的元素作为字符串中下标 step 的元素出现时,向四个方向传递,查找是否存在路径结果与字符串相同。

递归函数流程:

1. 遍历每个位置,标记当前位置并将当前位置的字母作为首字母进行递归,并且在回溯时撤回标记。

2. 在每个递归的状态中,我们维护⼀个步数 step,表示当前已经处理了几个字母。

  • 若当前位置的字母与字符串中的第 step 个字母不相等,则返回 false。
  • 若当前 step 的值与字符串长度相等,表示存在⼀种路径使得 word 成立,返回 true。

3. 对当前位置的上下左右四个相邻位置进行递归,若递归结果为 true,则返回 true。

4. 若相邻的四个位置的递归结果都为 false,则返回 false。

  • 特别地,如果使用将当前遍历到的字符赋值为空格,并在回溯时恢复为原来的字母的方法,则在递归时不会重复遍历当前元素,可达到不使用标记数组的目的。

🌴算法代码:

class Solution 
{
    bool vis[7][7];
    int m, n;
public:
    bool exist(vector<vector<char>>& board, string word) 
    {
        m = board.size(), n = board[0].size();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
            {
                if (board[i][j] == word[0])
                {
                    vis[i][j] = true;
                    if (dfs(board, i, j, word, 1)) return true;
                    vis[i][j] = false;
                }
            }
        return false;
    }

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

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

        // 向量的方式,定义上下左右四个位置
        for (int k = 0; k < 4; k++)
        {
            int x = i + dx[k], y = j + dy[k];
            if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && board[x][y] == word[pos])
            {
                vis[x][y] = true;
                if (dfs(board, x, y, word, pos + 1)) return true;
                vis[x][y] = false;
            }
        }
        return false;
    }
};

一、黄金矿工

1.题目链接:1219. 黄金矿工

2.题目描述:

你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 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 个单元格中有黄金。

3.解法

🌴算法思路:

        枚举矩阵中所有的位置当成起点,来⼀次深度优先遍历,统计出所有情况下能收集到的黄金数的最大值即可。

🌴算法代码:

class Solution 
{
    bool vis[16][16];
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {1, -1, 0, 0};
    int m, n;
    int ret;
public:
    int getMaximumGold(vector<vector<int>>& grid) 
    {
        m = grid.size(), n = grid[0].size();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
            {
                if (grid[i][j])
                {
                    vis[i][j] = true;
                    dfs (grid, i, j, grid[i][j]);
                    vis[i][j] = false;
                }
            }
            return ret;
    }
    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 < m && y >= 0 && y < n && !vis[x][y] && grid[x][y])
            {
                vis[x][y] = true;
                dfs(grid, x, y, path + grid[x][y]);
                vis[x][y] = false;
            }
        }
    }
};

一、不同路径 III

1.题目链接:​​​​​​​980. 不同路径 III

2.题目描述:

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

  • 1 表示起始方格。且只有一个起始方格。
  • 2 表示结束方格,且只有一个结束方格。
  • 0 表示我们可以走过的空方格。
  • -1 表示我们无法跨越的障碍。

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

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

示例 1:

输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
输出:2
解释:我们有以下两条路径:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,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,0,0,0],[0,0,0,0],[0,0,0,2]]
输出:4
解释:我们有以下四条路径: 
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
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)
4. (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

3.解法

🌴算法思路:

        对于四个方向,我们可以定义⼀个二维数组 next ,大小为 4 ,每⼀维存储四个方向的坐标偏移量(详见代码)。题目要求到达目标位置时所有无障碍方格都存在路径中,我们可以定义⼀个变量记录 num 当前状态中剩余的未走过的无障碍方格个数,则当我们走到目标地点时只需要判断 num 是否为 0 即可。在移动时需要判断是否越界。


递归函数设计:void dfs(vector<vector<int>>& grid, int x, int y, int num)

参数:x,y(当前需要处理元素的坐标),num(当前剩余无障碍方格个数);

返回值:无;

函数作用:判断当前位置的四个方向是否可以添加至当前状态,查找在满足条件下从起始方格到结束方格的不同路径的数目。


递归流程如下:

1. 递归结束条件:当前位置的元素值为 2,若此时可走的位置数量 num 的值为 0,则 cnt 的值加一;

2. 遍历四个方向,若移动后未越界,无障碍并且未被标记,则标记当前位置,并递归移动后的位置,在回溯时撤销标记操作。

🌴算法代码:

class Solution 
{
    bool vis[21][21];
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int ret;
    int m, n, step;

public:
    int uniquePathsIII(vector<vector<int>>& grid) 
    {
        m = grid.size(), n = grid[0].size();
        int bx = 0, by = 0;
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j] == 0)
                    step++;
                else if (grid[i][j] == 1) 
                {
                    bx = i;
                    by = j;
                }
        step += 2;
        vis[bx][by] = true;
        dfs(grid, bx, by, 1);
        return ret;
    }
    
    void dfs(vector<vector<int>>& grid, int i, int j, int count) 
    {
        if (grid[i][j] == 2) 
        {
            if (count == step) // 判断是否合法
                ret++;
            return;
        }
        for (int k = 0; k < 4; k++) 
        {
            int x = i + dx[k], y = j + dy[k];
            if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] != -1) 
            {
                vis[x][y] = true;
                dfs(grid, x, y, count + 1);
                vis[x][y] = false;
            }
        }
    }
};
  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南风与鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值