Loading

leetcode576. 出界的路径数

题目链接:https://leetcode-cn.com/problems/out-of-boundary-paths/

记忆化搜索

用dfs函数递归来得到所有解,递归出口为:

  • 1.出界了,返回1,路径数+1
  • 2.k为0,没有移动次数了,返回0

cache[x][y][k]表示处于坐标(x,y)且还有k次移动次数时能出界的路径数,directions数组包含了四个可移动的方向。

C++代码:

class Solution {
public:
    int MOD = 1e9+7;
    //因为要在dfs函数里用到m n max,所以需要复制一下
    int _m,_n,_max;
    int cache[51][51][51];
    vector<vector<int>> directions = {{1,0}, {-1,0}, {0,1}, {0,-1}};

    int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
        _m = m; _n = n; _max = maxMove;
        //全都初始化为-1,表示没有到过这个坐标
        //这个memset的用法很妙,memset是以unsigned char的大小初始化的
	//而-1刚好是1111 1111,对于int来说也就是4个1111 1111,刚好也表示-1
        memset(cache,-1,sizeof(cache));
        return dfs(startRow,startColumn,maxMove);
    }

    int dfs(int x, int y, int k){
        //如果越界,证明是一种路径,返回1
        if(x<0 || x>=_m || y<0 || y>=_n) return 1;
        //如果k的次数用完还未出界,证明无路径可行
        if(k == 0) return 0;
        //如果重复到达一个坐标,直接返回该坐标出界的路径数
        if(cache[x][y][k] != -1) return cache[x][y][k];

        int ans = 0;
        //遍历一个坐标的四个方向
        for(auto& direction : directions){
            ans += dfs(x+direction[0], y+direction[1], k-1);
            ans %= MOD;
        }
        //记录该点路径数
        cache[x][y][k] = ans;
        return ans;
    }
};

动态规划

动规五部曲:

  1. 确定dp数组的定义以及下标的含义
    dp[i][j][k]表示球移动i次后到达坐标[j,k]的路径数。

  2. 递推公式

  • 如果移动i+1次没有出界,就把当前位置的坐标数全都
    dp[i+1][j'][k'] += dp[i][j][k];
  • 如果移动i+1次后出界,
    outCounts += dp[i][j][k]
  1. dp数组的初始化
    i=0时位于起始点,所以dp[0][startRow][startColumn] = 1,其他位置都初始化为0,等待计算

  2. 遍历顺序
    根据移动次数从小到大进行遍历,也就是i从0到maxMove,j和k的顺序无所谓,只要遍历完所有坐标就可以,这里都从0开始

  3. 举例推导dp数组
    除了dp[0][startRow][startColumn]=1,其他所有的dp[0][][]都为0,所以i=0时只会遍历这一个坐标,i=i+1时会遍历起始点周围的四个坐标,以此类推。

class Solution {
public:
    static constexpr int MOD = 1'000'000'007;

    int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
        vector<vector<int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        int outCounts = 0;
        vector<vector<vector<int>>> dp(maxMove + 1, vector<vector<int>>(m, vector<int>(n)));
        dp[0][startRow][startColumn] = 1;
        for (int i = 0; i < maxMove; i++) {
            for (int j = 0; j < m; j++) {
                for (int k = 0; k < n; k++) {
                    int count = dp[i][j][k];
                    if (count > 0) {
                        for (auto &direction : directions) {
                            int j1 = j + direction[0], k1 = k + direction[1];
                            if (j1 >= 0 && j1 < m && k1 >= 0 && k1 < n) {
                                dp[i + 1][j1][k1] = (dp[i + 1][j1][k1] + count) % MOD;
                            } else {
                                outCounts = (outCounts + count) % MOD;
                            }
                        }
                    }
                }
            }
        }
        return outCounts;
    }
};

时间复杂度和空间复杂度都是O(maxMove × m × n)
可以看到注意到dp[i][][]只在计算dp[i+1][][]时会用到,因此可以将dp中的移动次数的维度省略,将空间复杂度优化到 O(m×n)。
也就是用一个新的dpNew[][]数组来代替dp[i+1][][]。

class Solution {
public:
    static constexpr int MOD = 1'000'000'007;

    int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
        vector<vector<int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        int outCounts = 0;
        vector<vector<int>> dp(m, vector<int>(n));
        dp[startRow][startColumn] = 1;
        for (int i = 0; i < maxMove; i++) {
            vector<vector<int>> dpNew(m, vector<int>(n));
            for (int j = 0; j < m; j++) {
                for (int k = 0; k < n; k++) {
                    int count = dp[j][k];
                    if (count > 0) {
                        for (auto& direction : directions) {
                            int j1 = j + direction[0], k1 = k + direction[1];
                            if (j1 >= 0 && j1 < m && k1 >= 0 && k1 < n) {
                                dpNew[j1][k1] = (dpNew[j1][k1] + count) % MOD;
                            } else {
                                outCounts = (outCounts + count) % MOD;
                            }
                        }
                    }
                }
            }
            dp = dpNew;
        }
        return outCounts;
    }
};
posted @ 2021-08-15 16:09  泠枫Jun  阅读(48)  评论(0编辑  收藏  举报