【递推型 dp】B003_LC_停在原地的方案数(记忆化 / dp)

一、Problem

You have a pointer at index 0 in an array of size arrLen. At each step, you can move 1 position to the left, 1 position to the right in the array or stay in the same place (The pointer should not be placed outside the array at any time).

Given two integers steps and arrLen, return the number of ways such that your pointer still at index 0 after exactly steps steps.

Since the answer may be too large, return it modulo 10^9 + 7.

Input: steps = 3, arrLen = 2
Output: 4
Explanation: There are 4 differents ways to stay at index 0 after 3 steps.
Right, Left, Stay
Stay, Right, Left
Right, Stay, Left
Stay, Stay, Stay

Constraints:

1 <= steps <= 500
1 <= arrLen <= 10^6

二、Solution

方法一:记忆化 + 剪枝

这是我一开始的想法,但是遇到了许多坑:

溢出坑:a、b、c 的值都如果到了 1e9,那么三数相加一定会爆 int,这是第一版代码

class Solution {
    int N, MOD = (int) 1e9+7, f[][];
    int dfs(int i, int s) {
        if (i < 0 || i >= N)
            return 0;
        if (i != 0 && s <= 0)
            return 0;
        if (i == 0 && s == 0)
            return 1;
        if (f[i][s] != 0)
            return f[i][s];
        int a = dfs(i-1, s-1), b = dfs(i, s-1), c = dfs(i+1, s-1);
        return f[i][s] = (a + b + c) % MOD;
    }
    public int numWays(int steps, int arrLen) {
        N = arrLen;
        f =  new int[N+1][steps+1];
        dfs(0, steps);
        return f[0][steps];
    }
}

超时坑:将 int 改为 long 之后,发现我需要剪枝了

class Solution {
    int N;
    long MOD = (long) 1e9+7, f[][];
    long dfs(int i, int s) {
        if (i < 0 || i >= N)
            return 0;
        if (i != 0 && s <= 0)
            return 0;
        if (i == 0 && s == 0)
            return 1;
        if (f[i][s] != 0)
            return f[i][s] % MOD;
        long a = dfs(i-1, s-1) % MOD, b = dfs(i, s-1) % MOD, c = dfs(i+1, s-1) % MOD;
        return f[i][s] = (a + b + c) % MOD;
    }
    public int numWays(int steps, int arrLen) {
        N = arrLen;
        f =  new long[N+1][steps+1];
        dfs(0, steps);
        return (int)f[0][steps];
    }
}

超时2,剪枝后:当 i > s 时 无论如何,i 都回不到 0 位置了,所以当 i > s 时,返回 0 即可,但还是超时…

class Solution {
    int N;
    long MOD = (long) 1e9+7, f[][];
    long dfs(int i, int s) {
        if (i < 0 || i >= N)
            return 0;
        if (i > s)
            return 0;
        if (i == 0 && s == 0)
            return 1;
        if (f[i][s] != 0)
            return f[i][s] % MOD;
        long a = dfs(i-1, s-1) % MOD, b = dfs(i, s-1) % MOD, c = dfs(i+1, s-1) % MOD;
        return f[i][s] = (a + b + c) % MOD;
    }
    public int numWays(int steps, int arrLen) {
        N = arrLen;
        f =  new long[N+1][steps+1];
        dfs(0, steps);
        return (int)f[0][steps];
    }
}

最后发现我真的被坑埋了,看了 steps 才 500,但 arrLen却有 10610^6 这么大,咋话,如果 steps = 500,那么走过的步数大于 250 都是回不来的了。

最后 WA 了 3 次,勉强过了…

class Solution {
    int N;
    long MOD = (long) 1e9+7, f[][];
    long dfs(int i, int s) {
        if (i < 0 || i >= N)
            return 0;
        if (i > s)
            return 0;
        if (i == 0 && s == 0)
            return 1;
        if (f[i][s] != 0)
            return f[i][s] % MOD;
        long a = dfs(i-1, s-1) % MOD, b = dfs(i, s-1) % MOD, c = dfs(i+1, s-1) % MOD;
        return f[i][s] = (a + b + c) % MOD;
    }
    public int numWays(int steps, int arrLen) {
        N = Math.min(arrLen, steps/2+1);
        f =  new long[N+1][steps+1];
        dfs(0, steps);
        return (int)f[0][steps];
    }
}

复杂度分析

  • 时间复杂度:O(n×steps)O(n × steps)
  • 空间复杂度:O(n×steps)O(n × steps)

方法二:dp

  • 定义状态
    • f[i][j]f[i][j] 表示走了 ii 步,然后到达 jj 位置的方案数。
  • 思考初始化:
    • f[0][0]=1f[0][0] = 1 停留在原地必定有一种方案
  • 思考状态转移方程
    • f[i][j] =f[i1][j]f[i][j]\ = f[i-1][j]
    • f[i][j] +=f[i1][j1]f[i][j]\ += f[i-1][j-1]
    • f[i][j] +=f[i1][j+1]f[i][j]\ += f[i-1][j+1]
  • 思考输出f[steps][0]f[steps][0]
class Solution {
    public int numWays(int steps, int N) {
        N = Math.min(N, steps/2+1);
        int mod = (int) 1e9+7, f[][] = new int[steps+1][N];
        f[0][0] = 1;
        
        for (int i = 1; i <= steps; i++)
        for (int j = 0; j < N; j++) {
            f[i][j] = f[i-1][j];
            if (j > 0)   f[i][j] = (f[i][j] + f[i-1][j-1]) % mod;
            if (j+1 < N) f[i][j] = (f[i][j] + f[i-1][j+1]) % mod;
        }   
        return f[steps][0];
    }
}

复杂度分析

  • 时间复杂度:O(N×steps)O(N × steps)
  • 空间复杂度:O(N×steps)O(N × steps)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章