leetcode
leetcode 2351 ~ 2400
矩阵中严格递增的单元格数

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

难度:

标签:

题目描述

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

代码结果

运行时间: 704 ms, 内存: 54.7 MB


/*
 思路:
 1. 通过Java Stream来进行行和列的遍历。
 2. 使用DFS(深度优先搜索)来找到从一个单元格开始能够访问的最大单元格数量。
 3. 对于每一个单元格,检查其行和列中是否存在比其值更大的单元格。
 4. 使用动态规划记录每个单元格的访问最大数量以避免重复计算。
*/

import java.util.*;
import java.util.stream.IntStream;

public class Solution {
    private int m, n;
    private int[][] mat;
    private int[][] dp;

    public int maxCells(int[][] mat) {
        this.m = mat.length;
        this.n = mat[0].length;
        this.mat = mat;
        this.dp = new int[m][n];
        int maxCount = 0;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                maxCount = Math.max(maxCount, dfs(i, j));
            }
        }

        return maxCount;
    }

    private int dfs(int i, int j) {
        if (dp[i][j] != 0) return dp[i][j];

        int maxCount = 1;

        IntStream.range(0, m).filter(x -> mat[x][j] > mat[i][j]).forEach(x -> {
            maxCount = Math.max(maxCount, 1 + dfs(x, j));
        });

        IntStream.range(0, n).filter(y -> mat[i][y] > mat[i][j]).forEach(y -> {
            maxCount = Math.max(maxCount, 1 + dfs(i, y));
        });

        dp[i][j] = maxCount;
        return maxCount;
    }
}

解释

方法:

题解使用了动态规划和排序的方法来解决问题。首先,将矩阵中的所有单元格与它们的坐标一起存储到列表中,并按照单元格的值进行排序。接着,定义两个数组 rowMax 和 colMax 来存储每行和每列的最大可访问单元格数。通过遍历排序后的单元格列表,利用动态规划的思想,计算从每个单元格开始可以访问的最大单元格数量。如果当前单元格的值与前一个不同,则更新 rowMax 和 colMax。对于每个单元格,其可访问数是其所在行和列的最大可访问数中的最大值加一。最后,返回所有单元格的最大可访问数。

时间复杂度:

O(mn log(mn))

空间复杂度:

O(mn)

代码细节讲解

🦆
在题解中,为什么将所有单元格以它们的值进行排序?这个排序对解题思路有什么作用?
在这个题解中,将所有单元格按它们的值进行排序是为了确保在处理每一个单元格时,我们已经考虑了所有小于当前单元格值的单元格。这样的排序保证了在动态规划的过程中,我们从最小的值开始,向上扩展到更大的值,这是确保每个单元格被正确考虑在内,并计算出以该单元格为起点的最长递增路径的关键。通过这种方式,可以简化状态转移的复杂度,因为我们总是在已经处理过并存储了最优结果的单元格上进行构建。
🦆
题解提到使用动态规划,能否详细解释动态规划在此问题中的具体应用和状态转移方程?
在这个问题中,动态规划用于计算每个单元格作为起点的最长递增路径的长度。状态转移方程是通过比较当前单元格的最大可能递增路径与其所在行和列已知的最大路径长度。具体来说,对于每个单元格 (x, y),其最大递增路径为 `ans[x][y] = max(rowMax[x], colMax[y]) + 1`。这里,`rowMax[x]` 和 `colMax[y]` 分别表示在行x和列y中,截至当前单元格之前能达到的最大递增路径的长度。通过这种方式,我们可以确保利用已经计算过的信息来更新当前单元格的最大路径长度。
🦆
题解中提到更新rowMax和colMax数组,这个更新过程是如何确保不遗漏任何可能的路径的?
更新 `rowMax` 和 `colMax` 数组的过程中,这两个数组分别记录了当前行和列中所有已处理单元格的最大递增路径长度。当处理一个新的值时(即当前单元格的值与前一个单元格的值不同时),我们会遍历 `cache` 列表中暂存的单元格信息,这个列表包含了具有相同值的所有单元格的最大路径以及它们的坐标。通过这样的更新,我们确保在转移到新的单元格值时,已经充分利用了之前计算的结果,从而不会遗漏任何可能的递增路径。
🦆
题解中使用了一个cache列表来临时存储数据,在什么情况下需要清空cache,并将其值更新到rowMax和colMax数组?
在题解的实现中,`cache` 列表用于暂时存储在处理具有相同值的单元格群时计算得到的最大递增路径长度及其坐标。只有当我们在遍历过程中遇到一个新的单元格值时(即当前单元格的值大于前一个处理的单元格的值),我们才清空 `cache` 列表,并将其值更新到 `rowMax` 和 `colMax` 数组。这个更新是必要的,因为它标志着我们已经完成了对当前值所有单元格的处理,并且可以安全地更新行和列的最大值,为处理下一个更大值的单元格做准备。

相关问题