目录
一、单词搜索
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;
}
}
}
};