2684. 矩阵中移动的最大次数
题目
思路
这段代码实现了一个在二维网格上进行深度优先搜索(DFS)的算法,用来寻找从网格的左侧到任何位置所能达到的最大移动步数。移动规则是从当前位置只能向右上、正右或右下方向移动,并且移动的目标位置上的值必须大于当前位置上的值。算法的主要特点和步骤如下:
-
初始化:使用一个全局变量
ans
来记录遍历过程中找到的最大步数,初始值设为Integer.MIN_VALUE
。使用一个二维数组dp
来存储每个位置上能够达到的最大移动步数,以避免重复计算。 -
maxMoves
方法:这是主方法,接收一个二维数组grid
作为输入,代表网格。它首先初始化dp
数组,然后对网格的每一行的第一列进行遍历,从每个这样的位置出发调用DFS
方法。 -
DFS
方法:这个方法是深度优先搜索的核心,它尝试探索所有可能的移动路径,并更新全局最大值ans
。每次递归调用DFS
时,都会检查当前位置是否已经计算过(即dp[i][j]
不为0),如果是,则直接返回存储的值。它尝试向右上、正右、右下移动,只有在新位置的值大于当前位置的值时才会进行移动。 -
边界检查:
isOut
方法用于检查给定的位置是否越界(即是否在网格之外)。 -
动态规划(DP)是解决这个问题的关键技术之一。在这个特定的场景中,动态规划用于存储从网格中每个位置出发,符合移动规则的最长路径长度。这种方法可以避免重复计算已经解决过的子问题,极大地提高了算法的效率。下面是关于动态规划部分的详细解释:
动态规划数组的初始化
dp = new int[grid.length][grid[0].length];
:动态规划的数组dp
与输入的网格grid
具有相同的维度。数组中的每个元素dp[i][j]
初始值都为0,表示从位置(i, j)
出发的最长路径长度尚未计算。
使用动态规划存储中间结果
- 在
DFS
方法中,每次递归调用都会尝试计算从当前位置出发的最长路径长度。在达到一个新的位置(i, j)
时,首先检查dp[i][j]
是否不为0,即这个位置的最长路径是否已经计算过。如果已经计算过(dp[i][j]
不为0),则直接返回该值,这样就避免了对相同位置的重复计算。 - 如果
dp[i][j]
为0,说明这个位置的最长路径还未被计算。此时,算法会通过递归探索所有可能的移动方向,计算出从该位置出发的最长路径,并将计算结果存储在dp[i][j]
中。
更新动态规划数组
- 在探索完一个位置的所有可能移动后,选择最长路径长度,将其加1(当前位置也算作一步),然后更新
dp[i][j]
。这个加1操作是因为,不管接下来的路径有多长,从当前位置出发至少包含了一步移动。 - 这样,
dp[i][j]
就存储了从位置(i, j)
出发,按照给定移动规则能够达到的最长路径长度。这个值随后可被直接使用,避免了在后续搜索过程中对同一位置的重复计算。
- 更新全局最大值:在
DFS
过程中,每次递归都会尝试更新全局最大值ans
,这保证了在搜索结束时,ans
存储的是所有可能路径中的最大步数。
接下来,我将在代码中添加相应的注释,以便更好地理解每个部分的功能。
代码
class Solution {
public int ans = Integer.MIN_VALUE; // 存储最长路径的长度,初始化为Integer的最小值
public int[][] dp; // 用于动态规划的存储,记录每个位置的最长递增路径长度
// maxMoves函数初始化动态规划数组并开始遍历网格
public int maxMoves(int[][] grid) {
dp = new int[grid.length][grid[0].length]; // 初始化dp数组
for (int i = 0; i < grid.length; i++) { // 遍历每一行
DFS(grid, i, 0, 0); // 从每行的第一列开始深度优先搜索
}
return ans; // 返回找到的最长递增路径的长度
}
// DFS函数递归地搜索最长递增路径
public int DFS(int[][] grid, int i, int j, int len) {
ans = Math.max(ans, len); // 更新找到的最长路径长度
int cur = 0; // 当前位置的最长递增路径长度
if (dp[i][j] != 0) return dp[i][j]; // 如果当前位置已经计算过,直接返回结果
// 检查并递归地访问当前位置右上方的位置
if (!isOut(i - 1, j + 1, grid) && grid[i - 1][j + 1] > grid[i][j]) {
cur = Math.max(DFS(grid, i - 1, j + 1, len + 1), cur);
}
// 检查并递归地访问当前位置正右方的位置
if (!isOut(i, j + 1, grid) && grid[i][j + 1] > grid[i][j]) {
cur = Math.max(DFS(grid, i, j + 1, len + 1), cur);
}
// 检查并递归地访问当前位置右下方的位置
if (!isOut(i + 1, j + 1, grid) && grid[i + 1][j + 1] > grid[i][j]) {
cur = Math.max(DFS(grid, i + 1, j + 1, len + 1), cur);
}
dp[i][j] = cur + 1; // 更新当前位置的最长递增路径长度
if (cur == 0) {
return 0; // 如果当前位置没有递增路径,则返回0
}
return dp[i][j]; // 返回当前位置的最长递增路径长度
}
// isOut函数检查给定位置是否在网格外
public boolean isOut(int i, int j, int[][] grid) {
return i < 0 || i >= grid.length || j < 0 || j >= grid[0].length; // 检查索引是否越界
}
}