标签搜索

目 录CONTENT

文章目录

矩阵中的最长递增路径.md

小小城
2021-08-22 / 0 评论 / 0 点赞 / 4 阅读 / 4,771 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-02,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

矩阵中的最长递增路径

@[toc]

一、问题描述

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上
移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums = 
[
  [9,9,4],
  [6,6,8],
  [2,1,1]
] 
输出: 4 
解释: 最长递增路径为 [1, 2, 6, 9]。

示例 2:

输入: nums = 
[
  [3,4,5],
  [3,2,6],
  [2,2,1]
] 
输出: 4 
解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

方法一:朴素的深度优先搜索 【超时】

直觉

  • 深度优先搜索可以找到从任何单元格开始的最长递增路径。我们可以对全部单元格进行深度优先搜索。

算法

  • 每个单元格可以看作图 GG 中的一个定点。若两相邻细胞的值满足 a < b,则存在有向边 (a, b)。问题转化成:
  • 寻找有向图 GG 中的最长路径。
  • 显然,我们可以使用深度优先搜索或广度优先搜索从根开始访问连接的所有细胞。在搜索期间更新路径的最大长度,并在搜索完成后得到答案。
  • 一般而言,在深度优先搜索或广度优先搜索中,我们可以使用集合visited 来避免重复访问。在下一节中我们将介绍基于此的更优算法。
class Solution {
public:
    vector<vector<int>> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    int m, n;
    int dfs(vector<vector<int>>& matrix, int i, int j) 
    {
      int ans = 0;
      for ( auto d : dirs) 
      {
          int x = i + d[0], y = j + d[1];
          if (0 <= x && x < m && 0 <= y && y < n && matrix[x][y] > matrix[i][j])
              ans = max(ans, dfs(matrix, x, y));
      }
      return ++ans;
  }
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if (matrix.size() == 0) 
            return 0;
        m = matrix.size();
        n = matrix[0].size();
        int ans = 0;
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                ans = max(ans, dfs(matrix, i, j));
        return ans;
    }
};

方法二:记忆化深度优先搜索 【通过】

直觉

  • 将递归的结果存储下来,这样每个子问题只需要计算一次。

算法

  • 从上面的分析中,我们知道在淳朴的深度优先搜索方法中有许多重复的计算。

  • 一个优化途径是我们可以用一个集合来避免一次深度优先搜索中的重复访问。该优化可以将一次深度优先搜索的时间复杂度优化到 O(mn),总时间复杂度 O(m2 n2)

  • 下面介绍一个更有力的优化方法,记忆化

  • 在计算中,记忆化是一种优化技术,它通过存储“昂贵”的函数调用的结果,在相同的输入再次出现时返回缓存的结果,以此加快程序的速度。

  • 在本问题中,我们多次递归调用 dfs(x, y) 。但是,如果我们已经知道四个相邻单元格的结果,就只需要常数时间。在搜索过程中,如果未计算过单元格的结果,我们会计算并将其缓存;否则,直接从缓存中获取之.

class Solution {
public:
    int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    vector<vector<int> > memo;
    int dfs(vector<vector<int> >& matrix, int r, int c, int R, int C)
     {
        if (memo[r][c] != 0) 
        {
            return memo[r][c];
        }
        int t = 0;
        for (int i = 0; i < 4; ++i)
         {
            int nr = r + dirs[i][0];
            int nc = c + dirs[i][1];
            if (nr >= 0 && nr < R && nc >= 0 && nc < C && matrix[nr][nc] > matrix[r][c]) {
                t = max(t, dfs(matrix, nr, nc, R, C));
            }
        }
        memo[r][c] = 1 + t;
        return memo[r][c];
    }
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if (matrix.empty() || matrix[0].empty()) 
        	return 0;
        int R = matrix.size();
        int C = matrix[0].size();
        memo = vector<vector<int> >(R, vector<int>(C, 0));
        int res = 0;
        for (int i = 0; i < R; ++i)
         {
            for (int j = 0; j < C; ++j) 
            {
                res = max(res, dfs(matrix, i, j, R, C));
            }
        }
        return res;
    }
};


方法三:动态规划

class Point {
public:
    int x, y, v;
    Point(int ix, int iy, int iv) {
        x = ix;
        y = iy;
        v = iv;
    }
};

class Solution {
public:
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if (matrix.size() == 0)
            return 0;
        const int rows = matrix.size();
        const int cols = matrix[0].size();
        vector<vector<int>> dp(rows, vector<int>(cols, 1));
        vector<Point> vp;
        vp.reserve(rows * cols);
        for (int i = 0; i < rows; i++)
            for (int j = 0; j < cols; j++) {
                vp.push_back(Point(i, j, matrix[i][j]));
            }
        // 对坐标排序
        sort(vp.begin(), vp.end(), [](const Point &lhs, const Point &rhs){
            return lhs.v < rhs.v;
        });
        int ret = 1;
        for (const auto &p : vp) {
            auto x = p.x;
            auto y = p.y;
            auto v = p.v;
            // 寻找附近比当前矮的点的最高DP值
            if (x > 0 && matrix[x - 1][y] < v && dp[x - 1][y] + 1 > dp[x][y])
                dp[x][y] = dp[x - 1][y] + 1;
            if (x < rows - 1 && matrix[x + 1][y] < v && dp[x + 1][y] + 1 > dp[x][y])
                dp[x][y] = dp[x + 1][y] + 1;
            if (y > 0 && matrix[x][y - 1] < v && dp[x][y - 1] + 1 > dp[x][y])
                dp[x][y] = dp[x][y - 1] + 1;
            if (y < cols - 1 && matrix[x][y + 1] < v && dp[x][y + 1] + 1 > dp[x][y])
                dp[x][y] = dp[x][y + 1] + 1;
            if (dp[x][y] > ret)
                ret = dp[x][y];
        }
        return ret;
    }
};


方法四:广度优先搜索

  • 广度优先则不能完整的构造偏序关系。能做的是当位置(i,j)的路径长度被增加后,那么其上下左右的位置,潜在的路径长度都可能被修改。

  • 这些潜在的要修改的位置,可以存入队列。通过队列然后逐层更新路径长度。

算法的计算过程:

  • 先访问位置(0,0),值为9. 其长度为1. 其周围,没有要更新的位置。
  • 位置(0,1),值为9, 其长度为1. 其周围,没有要更新的位置。
  • 位置(0,2)值为4, 其长度为1。但是周围有位置(0,1)和位置(1,2)需要更新长度为2。但是 位置(0,1)和位置(1,2) 周围没有需要更新的位置。
  • 位置(1,0)值为6,长度为1。 周围有位置(0,0)需要更新长度为2。
  • 位置 (1,1)值为6,长度为1。 周围有 位置(0,1)和位置(1,2) 需要更新长度为2。
  • 位置(1,2)值为8,长度为2。 周围没有需要更新的位置。
  • 位置(2,0)值为2,长度为1。 周围有位置(1,0) 需要更新长度为2。 位置(1,0) 周围
  • 位置(0,0)需要更新长度为3.
  • 位置(2,1)值为1,长度为1。 周围有位置(2,0)和(1,1) 需要更新长度为2。 位置(1,0) ,(0,1),(1,2)需要更新长度为3. 位置(0,0)更新长度为4.
  • 逐层更新位置路径的最大长度。

用广度优先搜索,用队列,完成上述逐层更新的功能

    int R,C; 
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if(!(R=matrix.size()) || !(C=matrix[0].size())){
            return 0;
        }
        int ans = 0;
        vector<vector<int>> vis(R,vector<int>(C,0));
        for(int i=0;i<R;++i){
            for(int j=0;j<C;++j){
                if(!vis[i][j]){
                    ans = max(ans,bfs(matrix,vis,i,j));
                }
            }
        }
        return ans;
    }
    
    int bfs(const vector<vector<int>>& grid,
             vector<vector<int>>& vis,
             int x,int y
            ){
        vis[x][y] = 1;
        int max_h = 0;
        const static int dx[4]={1,-1,0,0};
        const static int dy[4]={0,0,1,-1};        
        typedef pair<int,int> Pair;
        queue<Pair> Q;
        Q.push(make_pair(x,y));
        while(Q.size()){
            Pair p = Q.front();Q.pop();
            const int & _x = p.first;
            const int & _y = p.second;
            max_h = max(max_h,vis[_x][_y]);
            for(int i=0;i<4;++i){
                const int nx = _x + dx[i];
                const int ny = _y + dy[i];
                if(in_grid(nx,ny) && grid[nx][ny] > grid[_x][_y]){
                    if(vis[_x][_y] + 1 > vis[nx][ny]){
                        vis[nx][ny] = vis[_x][_y] + 1;
                        Q.push(make_pair(nx,ny));
                    }
                }
            } 
        }
        return max_h;
    }
    
    bool in_grid(int x,int y){
        return x>=0 && x < R && y>=0 && y<C;
    }
0

评论区