leetcode
leetcode 2001 ~ 2050
网格图中递增路径的数目

网格图中递增路径的数目

难度:

标签:

题目描述

给你一个 m x n 的整数网格图 grid ,你可以从一个格子移动到 4 个方向相邻的任意一个格子。

请你返回在网格图中从 任意 格子出发,达到 任意 格子,且路径中的数字是 严格递增 的路径数目。由于答案可能会很大,请将结果对 109 + 7 取余 后返回。

如果两条路径中访问过的格子不是完全相同的,那么它们视为两条不同的路径。

 

示例 1:

输入:grid = [[1,1],[3,4]]
输出:8
解释:严格递增路径包括:
- 长度为 1 的路径:[1],[1],[3],[4] 。
- 长度为 2 的路径:[1 -> 3],[1 -> 4],[3 -> 4] 。
- 长度为 3 的路径:[1 -> 3 -> 4] 。
路径数目为 4 + 3 + 1 = 8 。

示例 2:

输入:grid = [[1],[2]]
输出:3
解释:严格递增路径包括:
- 长度为 1 的路径:[1],[2] 。
- 长度为 2 的路径:[1 -> 2] 。
路径数目为 2 + 1 = 3 。

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 1000
  • 1 <= m * n <= 105
  • 1 <= grid[i][j] <= 105

代码结果

运行时间: 642 ms, 内存: 31.7 MB


/*
 * 思路:
 * 1. 使用动态规划来记录从每个格子出发的严格递增路径数量。
 * 2. 按照格子内的值从小到大遍历所有格子,并更新相邻格子的路径数量。
 * 3. 使用Java Stream API进行格子排序和遍历。
 * 4. 使用模数运算(10^9 + 7)来防止结果溢出。
 */

import java.util.*;
import java.util.stream.Collectors;

public class Solution {
    private static final int MOD = 1000000007;
    private static final int[][] DIRS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    public int countPaths(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        List<int[]> cells = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                cells.add(new int[]{i, j});
            }
        }
        cells = cells.stream().sorted(Comparator.comparingInt(a -> grid[a[0]][a[1]])).collect(Collectors.toList());
        long result = 0;
        for (int[] cell : cells) {
            int i = cell[0];
            int j = cell[1];
            dp[i][j] = 1;
            for (int[] dir : DIRS) {
                int x = i + dir[0];
                int y = j + dir[1];
                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] > grid[i][j]) {
                    dp[i][j] = (dp[i][j] + dp[x][y]) % MOD;
                }
            }
            result = (result + dp[i][j]) % MOD;
        }
        return (int) result;
    }
}

解释

方法:

该题解采用了深度优先搜索(DFS)与记忆化的方法来解决问题。首先定义一个memo数组来存储每个格子出发的递增路径数量,避免重复计算。对于网格中的每个单元格,使用DFS探索所有可能的递增路径。通过递归调用,每次尝试向四个方向扩展路径,只有当新的单元格值大于当前单元格值时,才会继续递归搜索。每个单元格的递增路径数量是它自身(长度为1的路径)加上从它可以到达的四个方向上其他单元格的递增路径数量之和。最后,将所有单元格的路径数加起来得到总路径数。结果对1000000007取余以满足题目要求。

时间复杂度:

O(m*n)

空间复杂度:

O(m*n)

代码细节讲解

🦆
为什么在递归函数dfs中,每个单元格的初始计数设置为1?
在递归函数dfs中,每个单元格的初始计数设置为1是因为每个单元格本身可以视为一个长度为1的递增路径。这是路径计数的基础,确保在统计从该单元格出发的所有可能递增路径时,包括了单元格自身这一最短路径。
🦆
在进行DFS时,如何保证不会重复访问同一个单元格,从而避免形成环路?
在进行DFS时,通过设置条件只向值更大的单元格移动来保证不会重复访问同一个单元格,因而避免形成环路。具体来说,每次递归调用只考虑那些值大于当前单元格的相邻单元格,这样的搜索路径自然不会回到已访问的单元格,因为这将意味着值减小,不符合递增路径的要求。
🦆
为什么在每次增加路径数量后要对1000000007取余?这里使用MOD的原因是什么?
在每次增加路径数量后要对1000000007取余,是因为在处理大数据时,这样做可以防止整数溢出并减少计算所需的空间和时间。1000000007是一个大的质数,常用于编程竞赛和算法中作为模数,以确保结果在一个可管理的范围内。此外,MOD运算可以保证结果的一致性和安全性,特别是在可能需要网络传输或跨平台操作的情境下。
🦆
当网格的尺寸非常大时,DFS的递归深度可能会如何影响算法的性能?是否有可能导致栈溢出?
当网格尺寸非常大时,DFS的递归深度可能非常高,这可能导致算法性能下降,因为每一层递归都需要时间和空间来管理调用栈。在极端情况下,过深的递归调用可能导致栈溢出错误,尤其是在栈大小有限的系统中。为了应对这一问题,可以考虑使用迭代的深度优先搜索或广度优先搜索(BFS),或者在递归算法中引入迭代加深等技术,以控制递归深度并优化性能。

相关问题