Intro
-
LQ329和滑雪问题本质是一回事,一样的代码两者都可以通过。因为LQ329是找最长连续递增路径的长度,而滑雪问题则是找最长连续递减路径的长度,它们找的其实都是同一个路径。
-
值得注意的是,在LQ的题目当中,如果仅仅能让程序跑出正确的路径,得到正确答案还是不够的,这会出现超时错误。因此,需要维护一个记忆矩阵,或者叫备忘录,来记住从每个点开始进行搜索后,能到达的最大路径。当重复访问这个点的时候,就直接返回存在记忆矩阵中的值,不需要再从这个点,重新进行后面的遍历了。
-
这个备忘录不可能是遍历到最后得到的,应该是不断根据走出的路径,动态更新的。从某个起点出发后,多个点的
max_len
都会不断更新。- 在遍历过程中,肯定会多次经过同一个点如[x][y],算出多个max_len[x][y],这时候就取最大的。当遍历结束后,就自然找到了最大的max_len[x][y]
DFS
- 这里DFS不需要visited矩阵,因为它进行往下遍历的条件是递减,所以往后遍历的过程中,不会出现绕圈圈的情况。
class Solution
{
public:
int dfs(int x, int y)
{
if (maxlen[x][y] != -1)
{ //如果该点曾经计算过最大值
return maxlen[x][y]; //那么直接返回答案
}
for (int i = 0; i < 4; i++)
{
int newX = x + offset[i][0];
int newY = y + offset[i][1];
if (newX > -1 && newX < row && newY > -1 && newY < col && map[newX][newY] > map[x][y])
{
maxlen[x][y] = max(maxlen[x][y], dfs(newX, newY) + 1);
}
}
if (maxlen[x][y] == -1)
{ //如果已经是附近最大的
maxlen[x][y] = 1; //那么最长上升序列长度为1
}
return maxlen[x][y];
}
int longestIncreasingPath(vector<vector<int>> &matrix)
{
if (matrix.size() == 0 || matrix[0].size() == 0)
return 0;
memset(maxlen, -1, sizeof(maxlen));
row = matrix.size();
col = matrix[0].size();
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
map[i][j] = matrix[i][j];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
maxstep = max(maxstep, dfs(i, j));
return maxstep;
}
private:
int offset[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
int maxstep = 0;
int row, col;
int map[300][300];
int maxlen[300][300]; //maxlen[i][j]记录从(i, j)开始最大长度
};
BFS
- BFS的遍历需要注意的是不能用visited矩阵来标记是否被访问过。如果这么做了,遍历时是跑不出那个最长路径的。它只能跑出最短路径
class Solution {
public:
int map[500][500];
int offset[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int row, col;
int cnt[500][500]={0};
int maxstep=1 ;
int maxmat[300][300]; //从最初起点走到矩阵某个点中的最大长路径长度
struct Point{
int x;
int y;
int step;
Point(int a, int b, int c){
x=a;
y=b;
step=c;
}
};
queue<Point> bfsqueue;
void bfs(int x, int y){
Point point = Point(x,y,1);
bfsqueue.push(point);
maxmat[x][y]=maxmat[x][y]==0?1:maxmat[x][y];
while(!bfsqueue.empty()){
Point point = bfsqueue.front();
int curx=point.x;
int cury=point.y;
maxstep = max(maxstep, point.step);
bfsqueue.pop();
for(int i=0; i<4; i++){
int newX = point.x+offset[i][0];
int newY = point.y+offset[i][1];
if(newX >-1 && newX<row && newY>-1 && newY<col)
if (map[newX][newY]<map[curx][cury]){
if(maxmat[newX][newY] >= point.step+1) continue;
maxmat[newX][newY] = point.step+1;
Point tmpPt= Point(newX, newY, point.step+1);
bfsqueue.push(tmpPt);
}
}
}
return ;
}
int longestIncreasingPath(vector<vector<int>>& matrix) {
if (matrix.size()==0||matrix[0].size()==0) return 0;
memset(maxmat, 0 , sizeof(maxmat));
int max=0;
row=matrix.size();
col=matrix[0].size();
for(int i=0; i<row; i++)
for(int j=0; j<col; j++)
map[i][j]=matrix[i][j];
for(int i=0; i<row; i++)
for(int j=0; j<col; j++){
bfs(i,j);
}
return maxstep;
}
};
拓扑排序
- 思路就是找到入度为0的点,从它开始进行拓扑排序遍历,记下层数。
- 记录层数方法:
- 每次以层为单位入队遍历,即遍历到某层时,记录该层元素个数(队列的size),开一个for循环遍历
- 该方法也用在树的题目中
class Solution {
public:
int longestIncreasingPath(vector<vector<int>>& matrix) {
if(matrix.size()==0) return 0;
int m = matrix.size(), n = matrix[0].size();
degree = vector<vector<int>>(m, vector<int>(n));
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
for(int k=0; k<4; k++){
int new_x = i+dis[k][0];
int new_y = j+dis[k][1];
if(new_x>-1 && new_x<m && new_y>-1 && new_y<n && matrix[new_x][new_y]>matrix[i][j]){
degree[i][j]++;
}
}
queue<pair<int, int>> q;
for(int i=0; i<m; i++)
for(int j=0; j<n; j++){
if(degree[i][j]==0)
q.push({i,j});
}
int ans=0;
while(!q.empty()){
ans++;
int len = q.size();
for(int i=0; i<len; i++){
auto tmp = q.front();
q.pop();
int x = tmp.first, y = tmp.second;
for(int k=0; k<4; k++){
int new_x = x+dis[k][0];
int new_y = y+dis[k][1];
if(new_x>-1 && new_x<m && new_y>-1 && new_y<n && matrix[new_x][new_y]<matrix[x][y]){
degree[new_x][new_y]--;
if(degree[new_x][new_y]==0)
q.push({new_x, new_y});
}
}
}
}
return ans;
}
private:
int dis[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};
vector<vector<int>> degree;
};