Longest Increasing Path in a Matrix
题目
Given an integer matrix, find the length of the longest increasing path.
From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).
Example 1:
Input: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
Output: 4
Explanation: The longest increasing path is [1, 2, 6, 9].
Example 2:
Input: nums =
[
[3,4,5],
[3,2,6],
[2,2,1]
]
Output: 4
Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.
分析
首先,看到这个题目,我们会有一种似曾相识的感觉,这种感觉来源与求最长子序列这道题目,这道题目是动态规划的经典题目了。但这道题目与求最长子序列有点差别,首先,动态规划求最长子序列处理的是数组,这里处理的是矩阵。同时,数组遍历计算的方向只有一个,那就是往下标递增的顺序计算,而矩阵不同,对于非边界的元素,其计算的方向可以是上下左右四个方向。第二,经典的最长子序列求法是对于不连续递增的元素的,但这里递增的元素需要是连续相邻的位置的。
上面说到的差别应用在我们的实现中,具体表现为通过对矩阵遍历,求出每个点,也就是子问题的连续最长子序列,然后求另外点的最长子序列时用到已经求得的子问题的答案。也就是子问题加上1,就是下一个子问题的答案。
注意一点是,在矩阵下标没有越界的情况下,一个元素的四个方向都有可能有子问题,所以在遍历每个点的时候,都需要检查四个方向,对比四个方向子问题合成的当前问题的最优解。
这个过程与深度遍历有点类似,但一个明显的不同是,这里的动态规划算法保存了子问题的解,在需要利用都子问题的时候,从已经存有的子问题中找出答案即可,无需遍历做多无用的工。
源码
class Solution {
public:
int dfs(vector<vector<int>>& matrix, int row, int col, vector<vector<int> > &dp) {
if(dp[row][col] != 0) {
return dp[row][col];
}
int beginResult = 1;
int a[4] = {-1, 1, 0, 0};
int b[4] = {0, 0, -1, 1};
for(int i = 0; i < 4; i++) {
int currX = row + a[i];
int currY = col + b[i];
if(currX >= 0&&currX < matrix.size()&&currY >= 0&&currY < matrix[0].size()&&matrix[currX][currY] > matrix[row][col]) {
beginResult = max(beginResult, 1 + dfs(matrix, currX, currY, dp));
}
}
dp[row][col] = beginResult;
return dp[row][col];
}
int longestIncreasingPath(vector<vector<int>>& matrix) {
if(matrix.size() == 0||matrix[0].size() == 0) {
return 0;
}
int result = 0;
vector<vector<int> > dp(matrix.size(), vector<int>(matrix[0].size(), 0));
for(int i = 0; i < matrix.size(); i++) {
for(int j = 0; j < matrix[0].size(); j++) {
result = max(result, dfs(matrix, i, j, dp));
}
}
return result;
}
};
感悟
这个是动态规划经典例题的证明,对于能用其他方法解决的问题,如果能用动态规划来解决,解决的方案一般是比较佳的。但问题是动态规划比较难,变化非常多。其有两种基本的变形,第一种是自顶向下的,另外一种是自底向上的。这个问题是自顶向下的比较经典的问题,一般是采用递归解决,但这里的递归比一般的递归效率更高。