矩阵中严格递增的单元格数

给你一个下标从 1 开始、大小为 m x n 的整数矩阵 mat,你可以选择任一单元格作为 起始单元格
从起始单元格出发,你可以移动到同一行或同一列 中的任何其他单元格,但前提是目标单元格的值严格大于当前单元格的值
求能访问的最多单元格数

1. 动态规划(超时)

如果对每一个点进行递归搜索,时间复杂度会非常庞大
考虑使用动态规划减小时间复杂度
dp[i][j]表示以位置(i,j)为起点的最大路径单元格
最坏情况下每个元素都要横向纵向扫描一遍,时间复杂度为O(mn(m+n)),最后结果超时

假设dp[i][j]表示以位置(i,j)为终点的最大路径单元格
递归就变成了往前搜索,找更小的数,实质上跟上面没有区别
所以在方法二优化中,从小到大和从大到小的顺序没有影响

class Solution {
public:
    int m; int n;
    vector<vector<int>> dp;
    int maxIncreasingCells(vector<vector<int>>& mat) {
        //动态规划,记录每一个位置能访问的最大单元格
        m = mat.size(); n= mat[0].size();
        dp.resize(m,vector<int>(n));
        int res = 0;
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                res = max(res,dfs(mat,i,j));
        return res;

    }
    int dfs(vector<vector<int>>& mat,int x,int y){
        if(dp[x][y]) return dp[x][y];
        dp[x][y] = 1;
        for(int i=0;i<n;i++) //遍历同一行
            if(mat[x][i]>mat[x][y]) dp[x][y] = max(dp[x][y] ,dfs(mat,x,i)+1);
        for(int i=0;i<m;i++) //遍历同一列
            if(mat[i][y]>mat[x][y]) dp[x][y]  = max(dp[x][y] ,dfs(mat,i,y)+1);
        return dp[x][y];
    }
};

2. 动态规划优化

我们现在要想办法免去方法一的横纵向搜索过程
使时间复杂度优化到O(mn),必然要采取某种贪心思想
仔细观察方法一中的搜索过程,遍历同一行和同一列
是试图寻找同一行和同一列更大值的最大单元格数
我们可以直接记录更新记录该行和该列更大值的最大单元格数
这要求我们从大到小遍历所有值,并对行列最大值单元格进行更新
此时对应dp[i][j]表示以位置(i,j)为起点的最大路径单元格
搜索行列更大值,反过来就是从最大值从大到小更新记录

class Solution {
public:
    int maxIncreasingCells(vector<vector<int>> &mat) {
        int m = mat.size(), n = mat[0].size();
        vector<pair<int,int>> nums;
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                nums.push_back({mat[i][j],i*n+j});
        sort(nums.begin(),nums.end());
        int res = 0;
        vector<int> row_max(m), col_max(n);
        //按值从大到小访问
        for(int cnt=nums.size()-1;cnt>=0;){
            vector<pair<int,int>> same;; // 存储所有相同值,避免视作更小值
            int pre = nums[cnt].first;
            while(cnt>=0&&nums[cnt].first==pre){
                auto &[val,index] = nums[cnt]; cnt--;
                pre = val;//记录上一个值
                int i = index/n; int j = index%n;
                int updateval = max(row_max[i], col_max[j])+1; //获取最新的最大单元格数
                same.push_back({updateval,index});
                res = max(res,updateval);
            }
            for (int k = 0; k < same.size(); k++) {//遍历相同值,进行更新
                auto &[updateval, index] = same[k];
                int i = index/n; int j = index%n;
                row_max[i] = max(row_max[i], updateval); // 更新第 i 行的最大 f 值
                col_max[j] = max(col_max[j], updateval); // 更新第 j 列的最大 f 值
            }
        }
        return res;
    }
};

同样我们也能从小到大遍历所有值
此时对应dp[i][j]表示以(i,j)为终点的最大单元格数,搜索行列更小值
反过来就是从最小值从小到大更新行列最大单元格

class Solution {
public:
    int maxIncreasingCells(vector<vector<int>> &mat) {
        map<int, vector<pair<int, int>>> g; //这里使用红黑树进行排序
        int m = mat.size(), n = mat[0].size();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                g[mat[i][j]].emplace_back(i, j); // 相同元素放在同一组,统计位置,同时避免把相同值视为更大值,重复更新

        int ans = 0;
        vector<int> row_max(m), col_max(n);
        //按值从小到大访问
        for (auto &[_, pos]: g) {
            vector<int> mx; // 先把最大值算出来,再更新 row_max 和 col_max
            for (auto &[i, j]: pos) {//获得当前值的所有位置
                mx.push_back(max(row_max[i], col_max[j]) + 1); //这里暂存不直接进行更新,避免重复值多次更新
                ans = max(ans, mx.back());
            }
            for (int k = 0; k < pos.size(); k++) {//遍历所有位置,进行更新
                auto &[i, j] = pos[k];
                row_max[i] = max(row_max[i], mx[k]); // 更新第 i 行的最大 f 值
                col_max[j] = max(col_max[j], mx[k]); // 更新第 j 列的最大 f 值
            }
        }
        return ans;
    }
};
posted @ 2023-06-02 02:23  失控D大白兔  阅读(33)  评论(0编辑  收藏  举报