leetcode每日一题系列-出界的路径数「暴力dfs+动态规划」+周赛(1、2maybe3、4)

592 阅读2分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

leetcode-576-出界的路径数

[博客链接]

菜🐔的学习之路

掘金首页

[题目描述]

给你一个大小为 m x n 的网格和一个球。球的起始坐标为 [startRow, startColumn] 。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。你 最多 可以移动 maxMove 次球。

给你五个整数 mnmaxMovestartRow 以及 startColumn ,找出并返回可以将球移出边界的路径数量。因为答案可能非常大,返回对 109 + 7 取余 后的结果。

 

示例 1:

输入:m = 2, n = 2, maxMove = 2, startRow = 0, startColumn = 0
输出:6

示例 2:

输入:m = 1, n = 3, maxMove = 3, startRow = 0, startColumn = 1
输出:12

 

提示:

  • 1 <= m, n <= 50
  • 0 <= maxMove <= 50
  • 0 <= startRow < m
  • 0 <= startColumn < n
Related Topics
  • 动态规划
  • 👍 146 👎 0
  • 示例 1:

     输入:n = 4, preferences = [[1, 2, 3], [3, 2, 0], [3, 1, 0], [1, 2, 0]], pairs =
     [[0, 1], [2, 3]]
    输出:2
    解释:
    朋友 1 不开心,因为:
    - 10 配对,但 13 的亲近程度比 10 高,且
    - 31 的亲近程度比 32 高。
    朋友 3 不开心,因为:
    - 32 配对,但 31 的亲近程度比 32 高,且
    - 13 的亲近程度比 10 高。
    朋友 02 都是开心的。
    

    示例 2:

     输入:n = 2, preferences = [[1], [0]], pairs = [[1, 0]]
    输出:0
    解释:朋友 01 都开心。
    

    示例 3:

     输入:n = 4, preferences = [[1, 3, 2], [2, 3, 0], [1, 3, 0], [0, 2, 1]], pairs =
     [[1, 3], [0, 2]]
    输出:4
    

    [题目链接]

    leetcode题目链接

    [github地址]

    代码链接

    [思路介绍]

    思路一:暴力dfs+dp方程的来源分析

    • 定义4个方向变量,每一次移动都有四个方向,判断出界的条件有4个
    • x + dir[i][0] >= ex || x + dir[i][0] < 0 || y + dir[i][1] >= ey || y + dir[i][1] < 0
    • dfs递归求解
    • 定义计数器idx,不超过maxMove即满足条件
    • 这样时间复杂度是O(4maxMove4^{maxMove})
    • 根据题目所给出的数据范围是一定会TLE
    class Solution{
    int[][] dir = new int[][]{{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
            int res = 0, max = 0, ex = 0, ey = 0;
    
            public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
                //定义四个移动方向
                int mod = (int) 1e9 + 7, x = startRow, y = startColumn, idx = 0;
                max = maxMove;
                ex = m;
                ey = n;
                dfs(x, y, idx);
                return res % mod;
            }
    
            public void dfs(int x, int y, int idx) {
                //边界情况
                if (idx >= max) {
                    return;
                }
                int temp = idx;
                for (int i = 0; i < 4; i++) {
                    if (x + dir[i][0] >= ex || x + dir[i][0] < 0 || y + dir[i][1] >= ey || y + dir[i][1] < 0) {
                        res += 1;
                        continue;
                    }
                    dfs(x + dir[i][0], y + dir[i][1], temp + 1);
                }
            }
       }
    
    • 时间复杂度O(4maxMove4^{maxMove})
    • 空间复杂度O(4maxMove4^{maxMove})

    思路二:动态规划

    • 根据思路一不难发现dfs递归方程一共有三个变量
    • x, y, idx 分别表示
      • x:横坐标
      • y:纵坐标
      • idx:移动步数
    • 因此我们可以确定dp方程dp[idx][j][k]
    • 表示使用idx步从起始位置**(x, y)** 移动到 (j, k) 的方案数量
    • 初始化dp数组易得出dp[0][x][y] = 1
      • 表示使用0步从起点移动到起点只有一种方案
    • 其余状态的dp方程过程如下
    • dp[idx+1][j][k] = dp[idx][j-1][k] + dp[idx][j+1][k] + dp[idx][j][k-1] + dp[idx][j][k+1];
    • 上述dp方程更可以使用循环赋值的方式保证不漏
    • 也就是通过每个dp[idx][j][k] 求得其下一步移动四个方向后的dp[idx+1][j+dir[0]][k+dir[1]]
    • 这样方便计算
    public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
        int[][] dirs = new int[][]{{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
        int x = startRow, y = startColumn, mod = (int) 1e9 + 7;
        int res = 0;
        int[][][] dp = new int[maxMove + 1][m][n];
        //初始化
        dp[0][x][y] = 1;
        for (int i = 0; i < maxMove; i++) {
            for (int j = 0; j < m; j++) {
                for (int k = 0; k < n; k++) {
                    int cnt = dp[i][j][k];
                    if (cnt > 0) {
                        for (int[] dir : dirs
                        ) {
                            int mx = j + dir[0], my = k + dir[1];
                            if (mx >= 0 && mx < m && my >= 0 && my < n) {
                                dp[i + 1][mx][my] = (dp[i + 1][mx][my] + cnt) % mod;
                            } else {
                                res = (res + cnt) % mod;
                            }
                        }
                    }
                }
            }
        }
        return res;
    
    }
    
    • 时间复杂度O(maxMovemn)O(maxMove*m*n)
    • 空间复杂度O(maxMovemn)O(maxMove*m*n)

    思路三:还是补充一下记忆化递归吧

            public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
                //定义四个移动方向
                int x = startRow, y = startColumn, idx = 0;
                max = maxMove;
                ex = m;
                ey = n;
                return dfs(x, y, idx) % mod;
            }
    
            public int dfs(int x, int y, int idx) {
                String key = x + "," + y + "," + idx;
                if (map.containsKey(key)) {
                    return map.get(key);
                }
                //出界情况
                if (idx <= max && (x < 0 || x == ex || y < 0 || y == ey)) {
                    map.put(key, 1);
                    return 1;
                }
                //到达不了边界情况
                if (idx > max) {
                    map.put(key, 0);
                    return 0;
                }
                int temp = idx, ans = 0;
                for (int i = 0; i < 4; i++) {
                    ans = (ans + dfs(x + dir[i][0], y + dir[i][1], temp + 1)) % mod;
                }
                map.put(key, ans);
                return ans;
            }
    
    • 「TODO」周赛还未开始,晚上补题解