问题解析
题目要求在一个二维矩阵中,找到从任意位置出发的最大交替上坡/下坡路径。具体来说,你可以从一个位置开始,移动到相邻的格子(上下左右),要求路径上的每一步必须是交替的:上坡或者下坡。我们的目标是找到一条最长的路径,路径上的每个点都满足交替上坡和下坡的条件。
问题要点:
- 每个位置的高度给定为二维数组
a,其中a[i][j]表示位置(i, j)的高度。 - 从任意起点开始,移动到相邻的格子,要求是交替的上坡和下坡。
- 上坡定义为从较低的高度移动到较高的高度,反之为下坡。
- 我们需要返回可能的最大路径长度。
思路解析
-
DFS(深度优先搜索)+ 记忆化:
- 我们可以通过DFS来探索每一个可能的路径,递归地探索每一个位置的上下左右相邻的格子。
- 为了避免重复计算,我们使用记忆化,即每个位置
(x, y)的结果(从此位置开始的最大路径长度)只计算一次,并存储在一个二维数组dp中。 - 状态记录:我们通过
is_up(一个布尔值)来记录当前的坡度方向:True表示上坡,False表示下坡。
-
状态转移:
- 从一个位置出发,我们根据当前位置的高度和相邻格子的高度来决定是否可以移动。
- 如果当前位置
a[x][y]高于邻居格子的高度,且当前是在上坡状态,那么可以进行下坡(is_up=False)。 - 如果当前位置
a[x][y]低于邻居格子的高度,且当前是在下坡状态,那么可以进行上坡(is_up=True)。
-
递归过程:
- 从每个位置
(i, j)开始进行递归,分别考虑从此位置出发进行上坡和下坡的两种情况。 - 每次递归都会向相邻的四个方向(上、下、左、右)移动,满足上坡或下坡的条件。
- 从每个位置
-
边界检查:在DFS时要确保每次移动后的坐标
(nx, ny)不会越界。
代码详解
pythonCopy Code
def solution(m: int, n: int, a: list) -> int:
# 初始化记忆化数组,用于存储从每个位置出发的最大步数
dp = [[-1] * n for _ in range(m)]
def dfs(x: int, y: int, is_up: bool) -> int:
# 如果当前位置的结果已计算过,直接返回
if dp[x][y] != -1:
return dp[x][y]
# 初始化最大步数
max_steps = 0
# 遍历四个方向
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = x + dx, y + dy
# 确保新的位置在边界内且尚未访问
if 0 <= nx < m and 0 <= ny < n:
# 根据is_up的状态,判断是否可以移动
if is_up and a[nx][ny] < a[x][y]:
max_steps = max(max_steps, 1 + dfs(nx, ny, False)) # 下坡
elif not is_up and a[nx][ny] > a[x][y]:
max_steps = max(max_steps, 1 + dfs(nx, ny, True)) # 上坡
# 存储计算的结果
dp[x][y] = max_steps
return dp[x][y]
max_moves = 0
# 遍历所有位置,计算从每个点出发的最大步数
for i in range(m):
for j in range(n):
max_moves = max(max_moves, dfs(i, j, True), dfs(i, j, False))
return max_moves
if __name__ == '__main__':
print(solution(2, 2, [[1, 2], [4, 3]]) == 3)
print(solution(3, 3, [[10, 1, 6], [5, 9, 3], [7, 2, 4]]) == 8)
print(solution(4, 4, [[8, 3, 2, 1], [4, 7, 6, 5], [12, 11, 10, 9], [16, 15, 14, 13]]) == 11)
代码详解:
-
dp数组:dp[x][y]用来存储从位置(x, y)出发的最大路径长度。初始时所有位置的值都设置为-1,表示未计算过。
-
dfs(x, y, is_up):- 递归函数
dfs用来计算从位置(x, y)出发的最大路径长度。 is_up参数表示当前是否是在上坡状态,True表示上坡,False表示下坡。- 每次递归都计算当前点的最大步数,递归地遍历四个相邻的方向(上、下、左、右),并根据当前位置与相邻格子的高度差决定是否可以继续上坡或下坡。
- 递归函数
-
状态转移:
- 如果当前是上坡(
is_up=True),则可以向高度较低的邻居格子下坡。 - 如果当前是下坡(
is_up=False),则可以向高度较高的邻居格子上坡。
- 如果当前是上坡(
-
max_steps更新:- 在每次递归中,我们都检查并更新从当前位置出发的最大步数。每次递归调用都会增加步数并返回。
-
最终结果:
- 在主函数
solution中,我们遍历每个点,并分别计算从每个点出发的最大路径(上坡和下坡两种状态),最终返回所有计算路径中的最大值。
- 在主函数
图解:
假设我们有一个简单的 3x3 矩阵:
Copy Code
a = [ [10, 1, 6],
[5, 9, 3],
[7, 2, 4]
]
- 初始状态:从每个点开始,我们递归地寻找路径。
- DFS遍历:假设从位置
(0, 0)开始,首先考虑向下或向右移动,递归地探索每个方向。 - 记忆化:在每个递归过程中,存储每个位置的最大路径长度,避免重复计算。
复杂度分析:
-
时间复杂度:
- 每个位置
(x, y)在递归时最多被访问一次,每次递归的深度是有限的。因此,时间复杂度为O(m * n),其中m是行数,n是列数。
- 每个位置
-
空间复杂度:
- 除了输入矩阵
a,我们还使用了dp数组存储每个位置的最大路径,空间复杂度为O(m * n)。
- 除了输入矩阵
测试用例:
-
示例1:
Copy Code a = [[1, 2], [4, 3]] 输出:3路径:
(0,0) -> (0,1) -> (1,1)。 -
示例2:
Copy Code a = [[10, 1, 6], [5, 9, 3], [7, 2, 4]] 输出:8 -
示例3:
Copy Code a = [[8, 3, 2, 1], [4, 7, 6, 5], [12, 11, 10, 9], [16, 15, 14, 13]] 输出:11
通过这种DFS + 记忆化的方式,我们可以高效地解决这个问题,避免了重复计算,确保了较大的输入也能在合理的时间内得到解。