【Leetcode】576. Out of Boundary Paths

题目地址:

https://leetcode.com/problems/out-of-boundary-paths/

给定一个 m × n m\times n m×n的网格,在 ( i , j ) (i,j) (i,j)这个位置有个小球,其可以沿四个方向每步走一格。允许其走 N N N步,问其走出界的所有可能路径的路径总数是多少。注意,一旦走出界,之后再走的路径不作区分。例如 1 × 1 1\times 1 1×1的网格,小球在 ( 0 , 0 ) (0,0) (0,0)这个位置,则对任意 N ≥ 1 N\ge 1 N1,答案都是 4 4 4。最后答案要模 1 0 9 + 7 10^9+7 109+7之后返回。

记忆化搜索方法参考https://blog.csdn.net/qq_46105170/article/details/109181274。下面介绍动态规划方法:

f [ s ] [ i ] [ j ] f[s][i][j] f[s][i][j]是从 ( i , j ) (i,j) (i,j)出发,恰好走 s s s步能走出界的路径总数。那么初始条件 f [ 1 ] [ i ] [ j ] f[1][i][j] f[1][i][j]是一步出界的路径总数,很容易计算的。如果总共要走 s s s步,则可以按照第一步走到哪儿做划分,则有: f [ s ] [ i ] [ j ] = ∑ ( i , j ) → ( x , y ) f [ s − 1 ] [ x ] [ y ] f[s][i][j]=\sum_{(i,j)\to (x,y)} f[s-1][x][y] f[s][i][j]=(i,j)(x,y)f[s1][x][y]注意,这里的 ( x , y ) (x,y) (x,y)一定要在界内,因为 f f f的定义是“恰好”走 s s s步能走出界的路径总数。最后答案就是要求 ∑ k = 1 N f [ k ] [ i ] [ j ] \sum_{k=1}^{N}f[k][i][j] k=1Nf[k][i][j]代码如下:

public class Solution {
    public int findPaths(int m, int n, int N, int i, int j) {
        int MOD = (int) (1E9 + 7);
        
        // dp[k][i][j]是从(i, j)这个位置恰好走k步出界的路径数
        int[][][] dp = new int[N + 1][m][n];
        int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
        for (int step = 1; step <= N; step++) {
            for (int x = 0; x < m; x++) {
                for (int y = 0; y < n; y++) {
                    if (step == 1) {
                        for (int[] dir : dirs) {
                            int nextX = x + dir[0], nextY = y + dir[1];
                            if (!inBound(nextX, nextY, m, n)) {
                                dp[step][x][y]++;
                            }
                        }
                    } else {
                        for (int[] dir : dirs) {
                            int nextX = x + dir[0], nextY = y + dir[1];
                            if (inBound(nextX, nextY, m, n)) {
                                dp[step][x][y] += dp[step - 1][nextX][nextY];
                                dp[step][x][y] %= MOD;
                            }
                        }
                    }
                }
            }
        }
        
        int res = 0;
        for (int k = 0; k <= N; k++) {
            res = (res + dp[k][i][j]) % MOD;
        }
        
        return res;
    }
    
    private boolean inBound(int x, int y, int m, int n) {
        return 0 <= x && x < m && 0 <= y && y < n;
    }
}

时间复杂度 O ( N m n ) O(Nmn) O(Nmn)

可以用滚动数组做空间优化。优化的时候需要注意两点:
1、计算dp[step & 1][x][y]之前一定要清零,把上上层的值清掉,否则会得出错误答案;
2、每次计算完一层,就要将dp[step & 1][i][j]累加到答案里,由于滚动数组只保存两层数据,所以不能像上面的方法一样到最后再累加。
代码如下:

public class Solution {
    public int findPaths(int m, int n, int N, int i, int j) {
        int MOD = (int) (1E9 + 7);
        
        // dp[k][i][j]是从(i, j)这个位置恰好走k步出界的路径数
        int[][][] dp = new int[2][m][n];
        int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
        
        int res = 0;
        for (int step = 1; step <= N; step++) {
            for (int x = 0; x < m; x++) {
                for (int y = 0; y < n; y++) {
                	// 计算前先清零
                    dp[step & 1][x][y] = 0;
                    
                    if (step == 1) {
                        for (int[] dir : dirs) {
                            int nextX = x + dir[0], nextY = y + dir[1];
                            if (!inBound(nextX, nextY, m, n)) {
                                dp[step][x][y]++;
                            }
                        }
                    } else {
                        for (int[] dir : dirs) {
                            int nextX = x + dir[0], nextY = y + dir[1];
                            if (inBound(nextX, nextY, m, n)) {
                                dp[step & 1][x][y] += dp[step - 1 & 1][nextX][nextY];
                                dp[step & 1][x][y] %= MOD;
                            }
                        }
                    }
                }
            }
            
            // 一旦算完一层,立刻累加
            res = (res + dp[step & 1][i][j]) % MOD;
        }
        
        return res;
    }
    
    private boolean inBound(int x, int y, int m, int n) {
        return 0 <= x && x < m && 0 <= y && y < n;
    }
}

时间复杂度不变,空间 O ( m n ) O(mn) O(mn)

当然,状态的设置不是唯一的。也可以简单的设 f [ s ] [ i ] [ j ] f[s][i][j] f[s][i][j]就是从 ( i , j ) (i,j) (i,j)出发走 s s s步走出界的路径总数,那么可以按照下一步走到哪儿做分类,则有: f [ s ] [ i ] [ j ] = ∑ ( i , j ) → ( x , y ) f [ s − 1 ] [ x ] [ y ] f[s][i][j]=\sum_{(i,j)\to (x,y)} f[s-1][x][y] f[s][i][j]=(i,j)(x,y)f[s1][x][y]如果 ( x , y ) (x,y) (x,y)出界的话 f f f就取 1 1 1。最后返回 f [ N ] [ i ] [ j ] f[N][i][j] f[N][i][j]即可。下面直接给出滚动数组优化后的代码:

public class Solution {
    public int findPaths(int m, int n, int N, int i, int j) {
        int MOD = (int) (1E9 + 7);
        
        // dp[s][i][j]表示从(i, j)走s步走出界的路径总数
        int[][][] dp = new int[2][m][n];
    
        int[] dir = {1, 0, -1, 0, 1};
        for (int k = 1; k <= N; k++) {
            for (int x = 0; x < m; x++) {
                for (int y = 0; y < n; y++) {
                	// 先清零
                    dp[k & 1][x][y] = 0;
                    
                    for (int p = 0; p < 4; p++) {
                        int nextX = x + dir[p], nextY = y + dir[p + 1];
                        dp[k & 1][x][y] += inBound(nextX, nextY, m, n) ? dp[k - 1 & 1][nextX][nextY] : 1;
                        dp[k & 1][x][y] %= MOD;
                    }
                }
            }
        }
        
        return dp[N & 1][i][j];
    }
    
    private boolean inBound(int x, int y, int m, int n) {
        return 0 <= x && x < m && 0 <= y && y < n;
    }
}

时间复杂度一样,空间 O ( m n ) O(mn) O(mn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值