矩阵最长递增路径

题目描述

给定一个 n 行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。并输出这条最长路径的长度。

这个路径必须满足以下条件:

1. 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。

2. 你不能走重复的单元格。即每个格子最多只能走一次。

数据范围:1≤n,m≤1000,0≤matrix[i][j]≤1000

要求:空间复杂度 O(nm)O(nm) ,时间复杂度 O(nm)O(nm)

例如:当输入为[[1,2,3],[4,5,6],[7,8,9]]时,对应的输出为5, 其中的一条最长递增路径如下图所示:

深度优先搜索

核心代码

    vector<vector<int>> dp;
    int Max=0,n,m;
    array<int,4> x={1,-1,0,0};
    array<int,4> y={0,0,1,-1};
    int dfs(vector<vector<int>>& matrix,int row,int col)
    {
        dp[row][col]=1;
        for(int i=0;i<4;++i){
            int r=row+x[i];
            int c=col+y[i];
            if(r<n&&r>=0&&c<m&&c>=0&&matrix[row][col]<matrix[r][c]){
                if(!dp[r][c]) dp[r][c]=dfs(matrix,r,c);
                dp[row][col]=max(dp[row][col],dp[r][c]+1);
            }
        }
        Max=max(Max,dp[row][col]);
        return dp[row][col];
    }
    int solve(vector<vector<int> >& matrix) {
        n=matrix.size();
        m=matrix[0].size();
        dp=vector<vector<int>>(n,vector<int>(m,0));
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                if(!dp[i][j]) dfs(matrix,i,j);
            }
        }
        return Max;
    }

典型搜索题,又有点动态规划的味道。注意学习其中的代码tip:

  1. 对于这种向四个方向搜索的题目,可用方向数组(两个一维或一个二维)表示每次方向的变化,这样可省去大量重复代码;

  1. 在函数间传递vector,最好传递& vector,这样避免了复制的时间,当vector规模很大时,可大大缩短时间(本题就是将dfs参数列表中matrix修改为引用之后才AC的);

  1. 创建含指定个数重复元素的vector可参考如下代码:dp=vector<vector<int>>(n,vector<int>(m,0));

  1. 更新最值可采用max,min函数;

  1. Max要初始化为0。不要以为简单的小细节不值一提。

广度优先搜索

思路:

我们可以将矩阵看成是一个有向图,一个元素到另一个元素递增,代表有向图的箭头。这样我们可以根据有向图的出度入度找到最长的路径,且这个路径在矩阵中就是递增的。

具体做法:

  • step 1:计算每个节点(单元格)所对应的出度(符合边界条件且递增),对于作为边界条件的单元格,它的值比所有的相邻单元格的值都要大,因此作为边界条件的单元格的出度都为0。利用一个二维矩阵记录每个单元格的出度

  • step 2:利用拓扑排序的思想,从所有出度为0的单元格开始进行广度优先搜索。

  • step 3:借助队列来广度优先搜索,队列中每次加入出度为0的点,即路径最远点,每次从A点到B点,便将A点出度减一。

  • step 4:每次搜索都会遍历当前层的所有单元格,更新其余单元格的出度,并将出度变为0的单元格加入下一层搜索。

  • step 5:当搜索结束时,搜索的总层数即为矩阵中的最长递增路径的长度,因为bfs的层数就是路径增长的层数。

核心代码

    //记录四个方向
    int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    int n, m;
    int solve(vector<vector<int> >& matrix) {
        //空矩阵
        if (matrix.size() == 0 || matrix[0].size() == 0) 
            return 0;
        n = matrix.size();
        m = matrix[0].size();
        //记录每个单元的出度
        vector<vector<int> > outdegrees(n, vector<int> (m));
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                for (int k = 0; k < 4; k++) {
                    int nexti = i + dirs[k][0];
                    int nextj = j + dirs[k][1];
                    if (nexti >= 0 && nexti < n && nextj >= 0 && nextj < m && matrix[nexti][nextj] > matrix[i][j]) {
                        //符合条件,记录出度
                        outdegrees[i][j]++; 
                    }
                }
            }
        }
        queue <pair<int, int> > q;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                if (outdegrees[i][j] == 0)
                    //找到出度为0的入队列
                    q.push({i, j}); 
        int res = 0;
        while (!q.empty()) {
            res++;
            int size = q.size();
            for (int x = 0; x < size; x++) {
                pair<int, int> temp = q.front();
                q.pop();
                int i = temp.first;
                int j = temp.second;
                //四个方向
                for (int k = 0; k < 4; k++) {
                    int nexti = i + dirs[k][0];
                    int nextj = j + dirs[k][1];
                    //逆向搜索,所以下一步是小于
                    if (nexti >= 0 && nexti < n && nextj >= 0 && nextj < m && matrix[nexti][nextj] < matrix[i][j]) {
                        //符合条件,出度递减
                        outdegrees[nexti][nextj]--;
                        if (outdegrees[nexti][nextj] == 0) {
                            q.push({nexti, nextj});
                        }
                    }
                }
            }
        }
        return res;
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值