Skip to content

Commit 9286e92

Browse files
authored
Merge pull request #1301 from shreyash3087/add/leetcode-576
Docs: Added Solutions to Leetcode 576
2 parents bd92075 + 9316c33 commit 9286e92

File tree

2 files changed

+234
-1
lines changed

2 files changed

+234
-1
lines changed

dsa-problems/leetcode-problems/0500-0599.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ export const problems =[
428428
"problemName": "576. Out of Boundary Paths",
429429
"difficulty": "Medium",
430430
"leetCodeLink": "https://leetcode.com/problems/out-of-boundary-paths",
431-
"solutionLink": "#"
431+
"solutionLink": "/dsa-solutions/lc-solutions/0500-0599/out-of-boundary-paths"
432432
},
433433
{
434434
"problemName": "581. Shortest Unsorted Continuous Subarray",
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
---
2+
id: out-of-boundary-paths
3+
title: Out of Boundary Paths
4+
sidebar_label: 0576 - Out of Boundary Paths
5+
tags:
6+
- Memoization
7+
- Dynamic Programming
8+
- Recursion
9+
description: "This is a solution to the Out of Boundary Paths problem on LeetCode."
10+
---
11+
12+
## Problem Description
13+
14+
There is an `m x n` grid with a ball. The ball is initially at the position `[startRow, startColumn]`. You are allowed to move the ball to one of the four adjacent cells in the grid (possibly out of the grid crossing the grid boundary). You can apply **at most** `maxMove` moves to the ball.
15+
16+
Given the five integers `m`, `n`, `maxMove`, `startRow`, `startColumn`, return the number of paths to move the ball out of the grid boundary. Since the answer can be very large, return it modulo $10^9 + 7$.
17+
18+
### Examples
19+
20+
**Example 1:**
21+
22+
![image](https://assets.leetcode.com/uploads/2021/04/28/out_of_boundary_paths_1.png)
23+
```
24+
Input: m = 2, n = 2, maxMove = 2, startRow = 0, startColumn = 0
25+
Output: 6
26+
```
27+
28+
**Example 2:**
29+
30+
![image](https://assets.leetcode.com/uploads/2021/04/28/out_of_boundary_paths_2.png)
31+
```
32+
Input: m = 1, n = 3, maxMove = 3, startRow = 0, startColumn = 1
33+
Output: 12
34+
```
35+
36+
### Constraints
37+
38+
- `1 <= m, n <= 50`
39+
- `0 <= maxMove <= 50`
40+
- `0 <= startRow < m`
41+
- `0 <= startColumn < n`
42+
43+
## Solution for Out of Boundary Paths
44+
45+
### Approach 1: Brute Force [Time Limit Exceeded]
46+
#### Algorithm
47+
48+
In the brute force approach, we try to take one step in every direction and decrement the number of pending moves for each step taken. Whenever we reach out of the boundary while taking the steps, we deduce that one extra path is available to take the ball out.
49+
50+
In order to implement the same, we make use of a recursive function `findPaths(m,n,N,i,j)` which takes the current number of moves(N) along with the current position(i,j) as some of the parameters and returns the number of moves possible to take the ball out with the current pending moves from the current position. Now, we take a step in every direction and update the corresponding indices involved along with the current number of pending moves.
51+
52+
Further, if we run out of moves at any moment, we return a 0 indicating that the current set of moves doesn't take the ball out of boundary.
53+
54+
## Code in Different Languages
55+
56+
<Tabs>
57+
<TabItem value="cpp" label="C++">
58+
<SolutionAuthor name="@Shreyash3087"/>
59+
60+
```cpp
61+
class Solution {
62+
public:
63+
int findPaths(int m, int n, int N, int i, int j) {
64+
if (i == m || j == n || i < 0 || j < 0) return 1;
65+
if (N == 0) return 0;
66+
return findPaths(m, n, N - 1, i - 1, j)
67+
+ findPaths(m, n, N - 1, i + 1, j)
68+
+ findPaths(m, n, N - 1, i, j - 1)
69+
+ findPaths(m, n, N - 1, i, j + 1);
70+
}
71+
};
72+
73+
74+
```
75+
</TabItem>
76+
<TabItem value="java" label="Java">
77+
<SolutionAuthor name="@Shreyash3087"/>
78+
79+
```java
80+
class Solution {
81+
public int findPaths(int m, int n, int N, int i, int j) {
82+
if (i == m || j == n || i < 0 || j < 0) return 1;
83+
if (N == 0) return 0;
84+
return findPaths(m, n, N - 1, i - 1, j)
85+
+ findPaths(m, n, N - 1, i + 1, j)
86+
+ findPaths(m, n, N - 1, i, j - 1)
87+
+ findPaths(m, n, N - 1, i, j + 1);
88+
}
89+
}
90+
```
91+
92+
</TabItem>
93+
<TabItem value="python" label="Python">
94+
<SolutionAuthor name="@Shreyash3087"/>
95+
96+
```python
97+
class Solution:
98+
def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:
99+
if i == m or j == n or i < 0 or j < 0:
100+
return 1
101+
if N == 0:
102+
return 0
103+
return (self.findPaths(m, n, N - 1, i - 1, j) +
104+
self.findPaths(m, n, N - 1, i + 1, j) +
105+
self.findPaths(m, n, N - 1, i, j - 1) +
106+
self.findPaths(m, n, N - 1, i, j + 1))
107+
108+
```
109+
</TabItem>
110+
</Tabs>
111+
112+
## Complexity Analysis
113+
114+
### Time Complexity: $O(4^N)$
115+
116+
> **Reason**: Size of recursion tree will be $4^N$. Here, N refers to the number of moves allowed.
117+
118+
### Space Complexity: $O(N)$
119+
120+
> **Reason**: The depth of the recursion tree can go upto N.
121+
122+
### Approach 2: Recursion with Memoization
123+
#### Algorithm
124+
In the brute force approach, while going through the various branches of the recursion tree, we could reach the same position with the same number of moves left.
125+
126+
Thus, a lot of redundant function calls are made with the same set of parameters leading to a useless increase in runtime. We can remove this redundancy by making use of a memoization array, memo. memo[i][j][k] is used to store the number of possible moves leading to a path out of the boundary if the current position is given by the indices (i,j) and number of moves left is k.
127+
128+
Thus, now if a function call with some parameters is repeated, the memo array will already contain valid values corresponding to that function call resulting in pruning of the search space.
129+
130+
## Code in Different Languages
131+
132+
<Tabs>
133+
<TabItem value="cpp" label="C++">
134+
<SolutionAuthor name="@Shreyash3087"/>
135+
136+
```cpp
137+
#include <vector>
138+
#include <cstring> // For memset
139+
140+
class Solution {
141+
public:
142+
int M = 1000000007;
143+
144+
int findPaths(int m, int n, int N, int i, int j) {
145+
std::vector<std::vector<std::vector<int>>> memo(m, std::vector<std::vector<int>>(n, std::vector<int>(N + 1, -1)));
146+
return findPaths(m, n, N, i, j, memo);
147+
}
148+
149+
private:
150+
int findPaths(int m, int n, int N, int i, int j, std::vector<std::vector<std::vector<int>>>& memo) {
151+
if (i == m || j == n || i < 0 || j < 0) return 1;
152+
if (N == 0) return 0;
153+
if (memo[i][j][N] >= 0) return memo[i][j][N];
154+
memo[i][j][N] = (
155+
(findPaths(m, n, N - 1, i - 1, j, memo) + findPaths(m, n, N - 1, i + 1, j, memo)) % M +
156+
(findPaths(m, n, N - 1, i, j - 1, memo) + findPaths(m, n, N - 1, i, j + 1, memo)) % M
157+
) % M;
158+
return memo[i][j][N];
159+
}
160+
};
161+
162+
```
163+
</TabItem>
164+
<TabItem value="java" label="Java">
165+
<SolutionAuthor name="@Shreyash3087"/>
166+
167+
```java
168+
class Solution {
169+
int M = 1000000007;
170+
171+
public int findPaths(int m, int n, int N, int i, int j) {
172+
int[][][] memo = new int[m][n][N + 1];
173+
for (int[][] l : memo) for (int[] sl : l) Arrays.fill(sl, -1);
174+
return findPaths(m, n, N, i, j, memo);
175+
}
176+
177+
public int findPaths(int m, int n, int N, int i, int j, int[][][] memo) {
178+
if (i == m || j == n || i < 0 || j < 0) return 1;
179+
if (N == 0) return 0;
180+
if (memo[i][j][N] >= 0) return memo[i][j][N];
181+
memo[i][j][N] = (
182+
(findPaths(m, n, N - 1, i - 1, j, memo) + findPaths(m, n, N - 1, i + 1, j, memo)) % M +
183+
(findPaths(m, n, N - 1, i, j - 1, memo) + findPaths(m, n, N - 1, i, j + 1, memo)) % M
184+
) % M;
185+
return memo[i][j][N];
186+
}
187+
}
188+
```
189+
190+
</TabItem>
191+
<TabItem value="python" label="Python">
192+
<SolutionAuthor name="@Shreyash3087"/>
193+
194+
```python
195+
class Solution:
196+
M = 1000000007
197+
198+
def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:
199+
memo = [[[-1 for _ in range(N + 1)] for _ in range(n)] for _ in range(m)]
200+
return self.findPathsMemo(m, n, N, i, j, memo)
201+
202+
def findPathsMemo(self, m: int, n: int, N: int, i: int, j: int, memo: list) -> int:
203+
if i == m or j == n or i < 0 or j < 0:
204+
return 1
205+
if N == 0:
206+
return 0
207+
if memo[i][j][N] >= 0:
208+
return memo[i][j][N]
209+
memo[i][j][N] = (
210+
(self.findPathsMemo(m, n, N - 1, i - 1, j, memo) + self.findPathsMemo(m, n, N - 1, i + 1, j, memo)) % self.M +
211+
(self.findPathsMemo(m, n, N - 1, i, j - 1, memo) + self.findPathsMemo(m, n, N - 1, i, j + 1, memo)) % self.M
212+
) % self.M
213+
return memo[i][j][N]
214+
215+
```
216+
</TabItem>
217+
</Tabs>
218+
219+
## Complexity Analysis
220+
221+
### Time Complexity: $O(mnN)$
222+
223+
> **Reason**: We need to fill the memo array once with dimensions m×n×N. Here, m, n refer to the number of rows and columns of the given grid respectively. N refers to the total number of allowed moves.
224+
225+
### Space Complexity: $O(mnN)$
226+
227+
> **Reason**: memo array of size m×n×N is used.
228+
229+
## References
230+
231+
- **LeetCode Problem**: [Out of Boundary Paths](https://leetcode.com/problems/out-of-boundary-paths/description/)
232+
233+
- **Solution Link**: [Out of Boundary Paths](https://leetcode.com/problems/out-of-boundary-paths/solutions/)

0 commit comments

Comments
 (0)