title: “Frog Jump Problem: Dynamic Programming Optimization” date: 2021-11-20 last_modified_at: 2026-06-23 categories: algorithms tags: [Algorithm, Dynamic Programming, Python] lang: en —
Problem Statement
A frog can jump up $1$ step, $2$ steps, …, up to $m$ steps at a time. Write a function to calculate the total number of unique ways the frog can jump to the top of an $n$-level staircase.
- Input: $n$ (total steps), $m$ (maximum steps per jump)
- Output: Total number of unique jumping methods.
1. Mathematical Analysis & State Equations
Let $f(n)$ be the total number of unique ways to reach the $n$-th step.
The frog’s last jump to reach step $n$ could have been from step $n-1$, step $n-2$, …, or step $n-m$ (provided these step numbers are valid, i.e., greater than or equal to $0$). Therefore, the problem satisfies the following transition relations:
- When $n \le m$: The frog can reach step $n$ from any step from $0$ to $n-1$. \(f(n) = f(n-1) + f(n-2) + \dots + f(0)\)
- When $n > m$: The frog can only jump from at most $m$ steps back. \(f(n) = f(n-1) + f(n-2) + \dots + f(n-m)\)
Base Case: $f(0) = 1$ (There is exactly $1$ way to stay at the ground level: doing nothing).
2. Dynamic Programming Implementation (O(n * m))
Instead of utilizing deep recursion with backtracking which results in massive redundant calculations ($O(m^n)$), we can use an array dp of size $n+1$ to store calculated states.
def frog_jump_dp(n: int, m: int) -> int:
if n < 0:
return 0
if n == 0 or n == 1:
return 1
# Initialize DP table, dp[i] stores the ways to reach step i
dp = [0] * (n + 1)
dp[0] = 1 # Base case
for i in range(1, n + 1):
# The frog can come from any of the previous min(i, m) steps
for j in range(1, min(i, m) + 1):
dp[i] += dp[i - j]
return dp[n]
# Example execution
if __name__ == '__main__':
n = int(input("Enter the total number of steps (n): "))
m = int(input("Enter the maximum jump height (m): "))
print(f"Total unique jumping methods: {frog_jump_dp(n, m)}")
3. Mathematical Optimization to O(n)
We can optimize the transition formula further to remove the inner loop entirely.
When $n > m$:
\[\begin{aligned} f(n) &= f(n-1) + f(n-2) + \dots + f(n-m) \\ f(n-1) &= f(n-2) + f(n-3) + \dots + f(n-1-m) \end{aligned}\]By substituting the second equation into the first, we get:
\[f(n) = 2 \times f(n-1) - f(n-1-m)\]When $n \le m$, it simplifies directly to the classic generalized variant:
\[f(n) = 2 \times f(n-1)\]Using this sliding window logic, the time complexity drops cleanly to $O(n)$.
def frog_jump_optimized(n: int, m: int) -> int:
if n < 0: return 0
if n == 0 or n == 1: return 1
dp = [0] * (n + 1)
dp[0] = 1
dp[1] = 1
for i in range(2, n + 1):
if i <= m:
dp[i] = 2 * dp[i - 1]
else:
dp[i] = 2 * dp[i - 1] - dp[i - 1 - m]
return dp[n]
Summary of Complexities
| Implementation | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Backtracking (Original) | $O(m^n)$ | $O(n)$ | Redundant computations cause Stack Overflow for large $n$. |
| Basic DP | $O(n \times m)$ | $O(n)$ | Standard approach, robust and clear logic. |
| Sliding Window DP | $O(n)$ | $O(n)$ | Optimal approach via algebraic substitution. |
```