Leetcode 329. Longest Increasing Path in a Matrix && 滑雪问题 最简单的纯DFS/纯BFS/拓扑排序的解法

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;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值