LeetCode 5274. Number of Ways to Stay in the Same Place After Some Steps - Java - DP

題目鏈接:5274. 停在原地的方案數

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 109+710^9 + 7.

Example 1:

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

Example 2:

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

Example 3:

Input: steps = 4, arrLen = 2
Output: 8

Constraints:

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

題解

要想在第 n 步到達 0 點,那麼第 n-1 步必須在 0 點或者 1 點,
要想第 n-1 步在 0 點,那麼第 n-2 步必須在 0 點或者 1 點。
要想第 n-1 步在 1 點,那麼第 n-2 步必須在 0 點,1 點,2 點。

由此看出這是動態規劃的問題。

定義 int 型二維數組 dp[501][252],dp[i][j] 表示第 i 步到達 j 點的方案數。

因爲 steps 最大爲 500,下標從 0 開始,所以大小爲 501,
因爲 500 步之內想要回到 0 點,向右走最多 250 步,通過上面的步驟中第三行所示,需要向右多出一位,用來計算後面的值,所以要加 1,又因爲下標從 0 開始,所以加 2。故大小爲 252。

公式如下:dp[i][j]={1i=j=0dp[i1][j]+dp[i1][j+1]i>0j=0dp[i1][j1]+dp[i1][j]+dp[i1][j+1]i>0j>0dp[i][j]=\begin{cases}1&\text{}i=j=0\\dp[i-1][j]+dp[i-1][j+1]&\text{}i>0且j=0\\dp[i-1][j-1]+dp[i-1][j]+dp[i-1][j+1]&\text{}i>0且j>0\end{cases}

Example 1 計算步驟如下:

時間複雜度O(mmin(m2,n))O(m*min(\frac{m}{2}, n)),m 表示 steps,n 表示 arrLen。
空間複雜度O(1)O(1),因爲大小固定,與 steps,arrLen 無關。

Java代碼(第6次提交)

我發現最裏面的判斷執行的次數太多,而且好像可以提出來。

// 執行時間:7ms
class Solution {
    public int numWays(int steps, int arrLen) {
        int mod = 1000000007;
        int[][] dp = new int[501][252];
        dp[0][0] = 1;
        int right = 0;
        for (int i = 1; i <= steps; ++i) {
            right = Math.min(Math.min(i, steps - i), arrLen - 1);
            // 第0列只能加正上方和右上方,左上方出界了
            dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % mod;
            for (int j = 1; j <= right; ++j) {// 從1開始
                // 左上方和正上方
                dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;
                dp[i][j] = (dp[i][j] + dp[i - 1][j + 1]) % mod;// 右上方
            }
        }
        return dp[steps][0];
    }
}

Java代碼(第5次提交)

// 執行時間:9ms
class Solution {
    public int numWays(int steps, int arrLen) {
        int mod = 1000000007;
        int[][] dp = new int[501][252];
        dp[0][0] = 1;
        int right = 0;
        for (int i = 1; i <= steps; ++i) {
            // 將這裏直接用一行表示
            right = Math.min(Math.min(i, steps - i), arrLen - 1);
            for (int j = 0; j <= right; ++j) {
                dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;// 兩行合爲一行
                if (j > 0) {
                    // 兩行合爲一行
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % mod;
                }
                // 兩行合爲一行
                dp[i][j] = (dp[i][j] + dp[i - 1][j + 1]) % mod;
            }
        }
        return dp[steps][0];
    }
}

Java代碼(第4次提交)

// 執行時間:11ms
class Solution {
    public int numWays(int steps, int arrLen) {
        int mod = 1000000007;
        // 第3次太囉嗦,直接固定爲最大範圍更節省時間,快6ms,
        // 這說明計算比申請內存(這裏申請的空間也不大)更耗時
        int[][] dp = new int[501][252];
        dp[0][0] = 1;
        int right = 0;
        for (int i = 1; i <= steps; ++i) {
            right = Math.min(i, steps - i);
            right = Math.min(right, arrLen - 1);
            for (int j = 0; j <= right; ++j) {
                dp[i][j] += dp[i - 1][j];
                dp[i][j] %= mod;
                if (j > 0) {
                    dp[i][j] += dp[i - 1][j - 1];
                    dp[i][j] %= mod;
                }
                dp[i][j] += dp[i - 1][j + 1];
                dp[i][j] %= mod;
            }
        }
        return dp[steps][0];
    }
}

Java代碼(第3次提交)

其實步數最多爲 500 步,所以最多向右走 250 步,如果繼續向右走 1 步,那麼就無法回到原點。所以 steps >> 1,又因爲需要計算 dp[i][steps >> 1],所以右邊還需要 1 位,因此要加 1,又因爲這是下標(從 0 開始),所以最終是 (steps >> 1) + 2,但是又不能超過限定的範圍。所以就變成了下面寫的那樣。

// 執行用時:17ms
class Solution {
    public int numWays(int steps, int arrLen) {
        int mod = 1000000007;
        // 如此囉嗦
        int[][] dp = new int[steps + 1][Math.min((steps >> 1) + 2, arrLen + 1)];
        dp[0][0] = 1;
        int right = 0;
        for (int i = 1; i <= steps; ++i) {
            right = Math.min(i, steps - i);
            right = Math.min(right, arrLen - 1);
            for (int j = 0; j <= right; ++j) {
                dp[i][j] += dp[i - 1][j];
                dp[i][j] %= mod;
                if (j > 0) {
                    dp[i][j] += dp[i - 1][j - 1];
                    dp[i][j] %= mod;
                }
                dp[i][j] += dp[i - 1][j + 1];
                dp[i][j] %= mod;
            }
        }
        return dp[steps][0];
    }
}

Java代碼(第2次提交)

第1次提交的時候忘記將輸出刪掉,,,結果超時了。
第2次提交,內存超出限制。原因是 arrLen 達到 10610^6,導致數組過大。

class Solution {
    public int numWays(int steps, int arrLen) {
        int mod = 1000000007;
        int[][] dp = new int[steps + 1][arrLen + 1];// 這裏超出內存限制
        dp[0][0] = 1;
        int right = 0;
        for (int i = 1; i <= steps; ++i) {
            right = Math.min(i, steps - i);
            right = Math.min(right, arrLen - 1);
            for (int j = 0; j <= right; ++j) {
                dp[i][j] += dp[i - 1][j];
                dp[i][j] %= mod;
                if (j > 0) {
                    dp[i][j] += dp[i - 1][j - 1];
                    dp[i][j] %= mod;
                }
                dp[i][j] += dp[i - 1][j + 1];
                dp[i][j] %= mod;
            }
        }
        return dp[steps][0];
    }
    // private void print(int[][] dp) {
    //     for (int i = 0; i < dp.length; ++i) {
    //         for (int j = 0; j < dp[i].length; ++j) {
    //             System.out.print(dp[i][j] + " ");
    //         }
    //         System.out.println();
    //     }
    //     System.out.println();
    // }
}

原文鏈接:https://www.cnblogs.com/wowpH/p/11924796.html

- End - wowpH - cnblogs -
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章