Maximum Strictly Increasing Cells in a Matrix

发布时间 2023-06-02 16:17:28作者: onlyblues

Maximum Strictly Increasing Cells in a Matrix

Given a 1-indexed m x n integer matrix mat, you can select any cell in the matrix as your starting cell.

From the starting cell, you can move to any other cell in the same row or column, but only if the value of the destination cell is strictly greater than the value of the current cell. You can repeat this process as many times as possible, moving from cell to cell until you can no longer make any moves.

Your task is to find the maximum number of cells that you can visit in the matrix by starting from some cell.

Return an integer denoting the maximum number of cells that can be visited.

Example 1:

Input: mat = [[3,1],[3,4]]
Output: 2
Explanation: The image shows how we can visit 2 cells starting from row 1, column 2. It can be shown that we cannot visit more than 2 cells no matter where we start from, so the answer is 2. 

Example 2:

Input: mat = [[1,1],[1,1]]
Output: 1
Explanation: Since the cells must be strictly increasing, we can only visit one cell in this example. 

Example 3:

Input: mat = [[3,1,6],[-9,5,7]]
Output: 4
Explanation: The image above shows how we can visit 4 cells starting from row 2, column 1. It can be shown that we cannot visit more than 4 cells no matter where we start from, so the answer is 4. 

Constraints:

  • m == mat.length
  • n == mat[i].length
  • 1 <= m, n <= 105
  • 1 <= m * n <= 105
  • -105 <= mat[i][j] <= 105

 

解题思路

  首先很容易想到记忆化搜索。记当前格子的数值为$g_{x,y}$,那么我们可以转移到同一行中比$g_{x,y}$严格大的格子$g_{x,j}$,或者同一列中比$g_{x,y}$严格大的格子$g_{i,y}$。可以发现整个转移的过程格子的数值不断增大,因此不可能转移到之前访问过的格子,即(转移)图中不会存在环,所以可以用动态规划来求解。

  定义状态$f(x,y)$表示所有从$(x,y)$出发能转移到的格子所构成路径的集合,属性是路径长度的最大值。

  对于当前的格子$(x,y)$,如果直接暴力枚举可以转移到的格子,那么转移的时间复杂度为$O(n+m)$,由于一共有$n \times m$个状态,因此整个记忆化搜索的时间复杂度就是$O(nm(n + m))$,会超时。

  对于当前的格子$(x,y)$无非就是要找到第$x$行和第$y$列中所有严格大于$g_{x,y}$的格子$(x',y')$,然后再从$(x',y')$转移。可以发现在这个过程中数值小于等于$g_{x,y}$的格子完全不用考虑,永远只考虑数值严格大于$g_{x,y}$的格子,并且随着转移的次数越多,数值越大。因此想到能不能对矩阵中的格子按照数值从大到小排序,然后再按照数值从大到小来记忆化搜索,同时开一个数组$r[x]$表示第$x$行中已搜索过格子的$f(x,y)$的最大值,$c[y]$表示第$y$列中已搜索过格子的$f(x,y)$的最大值。因此当需要搜索格子$(x,y)$时,因为只需考虑第$x$行和第$y$列中严格大于$g_{x,y}$的格子,而这些格子之前已经搜索过了,并且在第$x$行中能转移到的最多格子为$r[x]$,在第$y$列中能转移到的最多格子为$c[y]$,因此就有$f(x,y) = \max \{ r[x], c[y] \} + 1$。然后更新$r[x] = \max \{ r[x], f(x,y) \}$,$c[y] = \max \{ c[y], f(x,y) \}$。可以发现整个过程直接枚举求解即可,不需要用到递归搜索。

  需要注意的是如果在排序后存在连续的数值相同的格子,即$g_{x_1,y_1} = g_{x_2,y_2} = \ldots$,那么我们需要把这些格子都找出来同时处理。否则某一行或某一列的$r$和$c$会被值相同的格子的$f(x,y)$更新,当再枚举到值相同且在同一行或同一列的格子时,可能就会被之前枚举过的值相同的格子更新,即转移到值相同的格子,这就出问题了。

  AC代码如下,时间复杂度为$O(nm \log{(nm)})$:

 1 class Solution {
 2 public:
 3     int maxIncreasingCells(vector<vector<int>>& mat) {
 4         int n = mat.size(), m = mat[0].size();
 5         vector<vector<int>> p;
 6         for (int i = 0; i < n; i++) {
 7             for (int j = 0; j < m; j++) {
 8                 p.push_back({mat[i][j], i, j});
 9             }
10         }
11         sort(p.begin(), p.end(), greater<vector<int>>());
12         vector<vector<int>> f(n, vector<int>(m));
13         vector<int> r(n), c(m);
14         for (int i = 0; i < p.size(); i++) {
15             int j = i;
16             while (j < p.size() && p[j][0] == p[i][0]) {
17                 j++;
18             }
19             for (int k = i; k < j; k++) {
20                 f[p[k][1]][p[k][2]] = max(r[p[k][1]], c[p[k][2]]) + 1;
21             }
22             for (int k = i; k < j; k++) {
23                 r[p[k][1]] = max(r[p[k][1]], f[p[k][1]][p[k][2]]);
24                 c[p[k][2]] = max(c[p[k][2]], f[p[k][1]][p[k][2]]);
25             }
26             i = j - 1;
27         }
28         int ret = 0;
29         for (int i = 0; i < n; i++) {
30             for (int j = 0; j < m; j++) {
31                 ret = max(ret, f[i][j]);
32             }
33         }
34         return ret;
35     }
36 };

 

参考资料

  动态规划+优化【力扣周赛 347】:https://www.bilibili.com/BV1fo4y1T7MQ/