Array
Dynamic Programming
Matrix
Description
You are given an m x n
integer array grid
. There is a robot initially located at the top-left corner (i.e., grid[0][0]
). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]
). The robot can only move either down or right at any point in time.
An obstacle and space are marked as 1
or 0
respectively in grid
. A path that the robot takes cannot include any square that is an obstacle.
Return the number of possible unique paths that the robot can take to reach the bottom-right corner .
The testcases are generated so that the answer will be less than or equal to 2 * 109
.
Example 1:
Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
Output: 2
Explanation: There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
Example 2:
Input: obstacleGrid = [[0,1],[0,0]]
Output: 1
Constraints:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j]
is 0
or 1
.
Solutions
Solution 1: Memoization Search
We design a function \(\textit{dfs}(i, j)\) to represent the number of paths from the grid \((i, j)\) to the grid \((m - 1, n - 1)\) . Here, \(m\) and \(n\) are the number of rows and columns of the grid, respectively.
The execution process of the function \(\textit{dfs}(i, j)\) is as follows:
If \(i \ge m\) or \(j \ge n\) , or \(\textit{obstacleGrid}[i][j] = 1\) , the number of paths is \(0\) ;
If \(i = m - 1\) and \(j = n - 1\) , the number of paths is \(1\) ;
Otherwise, the number of paths is \(\textit{dfs}(i + 1, j) + \textit{dfs}(i, j + 1)\) .
To avoid redundant calculations, we can use memoization.
The time complexity is \(O(m \times n)\) , and the space complexity is \(O(m \times n)\) . Here, \(m\) and \(n\) are the number of rows and columns of the grid, respectively.
Python3 Java C++ Go TypeScript Rust JavaScript
1
2
3
4
5
6
7
8
9
10
11
12 class Solution :
def uniquePathsWithObstacles ( self , obstacleGrid : List [ List [ int ]]) -> int :
@cache
def dfs ( i : int , j : int ) -> int :
if i >= m or j >= n or obstacleGrid [ i ][ j ]:
return 0
if i == m - 1 and j == n - 1 :
return 1
return dfs ( i + 1 , j ) + dfs ( i , j + 1 )
m , n = len ( obstacleGrid ), len ( obstacleGrid [ 0 ])
return dfs ( 0 , 0 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 class Solution {
private Integer [][] f ;
private int [][] obstacleGrid ;
private int m ;
private int n ;
public int uniquePathsWithObstacles ( int [][] obstacleGrid ) {
m = obstacleGrid . length ;
n = obstacleGrid [ 0 ] . length ;
this . obstacleGrid = obstacleGrid ;
f = new Integer [ m ][ n ] ;
return dfs ( 0 , 0 );
}
private int dfs ( int i , int j ) {
if ( i >= m || j >= n || obstacleGrid [ i ][ j ] == 1 ) {
return 0 ;
}
if ( i == m - 1 && j == n - 1 ) {
return 1 ;
}
if ( f [ i ][ j ] == null ) {
f [ i ][ j ] = dfs ( i + 1 , j ) + dfs ( i , j + 1 );
}
return f [ i ][ j ] ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 class Solution {
public :
int uniquePathsWithObstacles ( vector < vector < int >>& obstacleGrid ) {
int m = obstacleGrid . size (), n = obstacleGrid [ 0 ]. size ();
vector < vector < int >> f ( m , vector < int > ( n , -1 ));
auto dfs = [ & ]( this auto && dfs , int i , int j ) {
if ( i >= m || j >= n || obstacleGrid [ i ][ j ]) {
return 0 ;
}
if ( i == m - 1 && j == n - 1 ) {
return 1 ;
}
if ( f [ i ][ j ] == -1 ) {
f [ i ][ j ] = dfs ( i + 1 , j ) + dfs ( i , j + 1 );
}
return f [ i ][ j ];
};
return dfs ( 0 , 0 );
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 func uniquePathsWithObstacles ( obstacleGrid [][] int ) int {
m , n := len ( obstacleGrid ), len ( obstacleGrid [ 0 ])
f := make ([][] int , m )
for i := range f {
f [ i ] = make ([] int , n )
for j := range f [ i ] {
f [ i ][ j ] = - 1
}
}
var dfs func ( i , j int ) int
dfs = func ( i , j int ) int {
if i >= m || j >= n || obstacleGrid [ i ][ j ] == 1 {
return 0
}
if i == m - 1 && j == n - 1 {
return 1
}
if f [ i ][ j ] == - 1 {
f [ i ][ j ] = dfs ( i + 1 , j ) + dfs ( i , j + 1 )
}
return f [ i ][ j ]
}
return dfs ( 0 , 0 )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 function uniquePathsWithObstacles ( obstacleGrid : number [][]) : number {
const m = obstacleGrid . length ;
const n = obstacleGrid [ 0 ]. length ;
const f : number [][] = Array . from ({ length : m }, () => Array ( n ). fill ( - 1 ));
const dfs = ( i : number , j : number ) : number => {
if ( i >= m || j >= n || obstacleGrid [ i ][ j ] === 1 ) {
return 0 ;
}
if ( i === m - 1 && j === n - 1 ) {
return 1 ;
}
if ( f [ i ][ j ] === - 1 ) {
f [ i ][ j ] = dfs ( i + 1 , j ) + dfs ( i , j + 1 );
}
return f [ i ][ j ];
};
return dfs ( 0 , 0 );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 impl Solution {
pub fn unique_paths_with_obstacles ( obstacle_grid : Vec < Vec < i32 >> ) -> i32 {
let m = obstacle_grid . len ();
let n = obstacle_grid [ 0 ]. len ();
let mut f = vec! [ vec! [ - 1 ; n ]; m ];
Self :: dfs ( 0 , 0 , & obstacle_grid , & mut f )
}
fn dfs ( i : usize , j : usize , obstacle_grid : & Vec < Vec < i32 >> , f : & mut Vec < Vec < i32 >> ) -> i32 {
let m = obstacle_grid . len ();
let n = obstacle_grid [ 0 ]. len ();
if i >= m || j >= n || obstacle_grid [ i ][ j ] == 1 {
return 0 ;
}
if i == m - 1 && j == n - 1 {
return 1 ;
}
if f [ i ][ j ] != - 1 {
return f [ i ][ j ];
}
let down = Self :: dfs ( i + 1 , j , obstacle_grid , f );
let right = Self :: dfs ( i , j + 1 , obstacle_grid , f );
f [ i ][ j ] = down + right ;
f [ i ][ j ]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 /**
* @param {number[][]} obstacleGrid
* @return {number}
*/
var uniquePathsWithObstacles = function ( obstacleGrid ) {
const m = obstacleGrid . length ;
const n = obstacleGrid [ 0 ]. length ;
const f = Array . from ({ length : m }, () => Array ( n ). fill ( - 1 ));
const dfs = ( i , j ) => {
if ( i >= m || j >= n || obstacleGrid [ i ][ j ] === 1 ) {
return 0 ;
}
if ( i === m - 1 && j === n - 1 ) {
return 1 ;
}
if ( f [ i ][ j ] === - 1 ) {
f [ i ][ j ] = dfs ( i + 1 , j ) + dfs ( i , j + 1 );
}
return f [ i ][ j ];
};
return dfs ( 0 , 0 );
};
Solution 2: Dynamic Programming
We can use a dynamic programming approach by defining a 2D array \(f\) , where \(f[i][j]\) represents the number of paths from the grid \((0,0)\) to the grid \((i,j)\) .
We first initialize all values in the first column and the first row of \(f\) , then traverse the other rows and columns with two cases:
If \(\textit{obstacleGrid}[i][j] = 1\) , it means the number of paths is \(0\) , so \(f[i][j] = 0\) ;
If \(\textit{obstacleGrid}[i][j] = 0\) , then \(f[i][j] = f[i - 1][j] + f[i][j - 1]\) .
Finally, return \(f[m - 1][n - 1]\) .
The time complexity is \(O(m \times n)\) , and the space complexity is \(O(m \times n)\) . Here, \(m\) and \(n\) are the number of rows and columns of the grid, respectively.
Python3 Java C++ Go TypeScript Rust JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Solution :
def uniquePathsWithObstacles ( self , obstacleGrid : List [ List [ int ]]) -> int :
m , n = len ( obstacleGrid ), len ( obstacleGrid [ 0 ])
f = [[ 0 ] * n for _ in range ( m )]
for i in range ( m ):
if obstacleGrid [ i ][ 0 ] == 1 :
break
f [ i ][ 0 ] = 1
for j in range ( n ):
if obstacleGrid [ 0 ][ j ] == 1 :
break
f [ 0 ][ j ] = 1
for i in range ( 1 , m ):
for j in range ( 1 , n ):
if obstacleGrid [ i ][ j ] == 0 :
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ]
return f [ - 1 ][ - 1 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 class Solution {
public int uniquePathsWithObstacles ( int [][] obstacleGrid ) {
int m = obstacleGrid . length , n = obstacleGrid [ 0 ] . length ;
int [][] f = new int [ m ][ n ] ;
for ( int i = 0 ; i < m && obstacleGrid [ i ][ 0 ] == 0 ; ++ i ) {
f [ i ][ 0 ] = 1 ;
}
for ( int j = 0 ; j < n && obstacleGrid [ 0 ][ j ] == 0 ; ++ j ) {
f [ 0 ][ j ] = 1 ;
}
for ( int i = 1 ; i < m ; ++ i ) {
for ( int j = 1 ; j < n ; ++ j ) {
if ( obstacleGrid [ i ][ j ] == 0 ) {
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ] ;
}
}
}
return f [ m - 1 ][ n - 1 ] ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 class Solution {
public :
int uniquePathsWithObstacles ( vector < vector < int >>& obstacleGrid ) {
int m = obstacleGrid . size (), n = obstacleGrid [ 0 ]. size ();
vector < vector < int >> f ( m , vector < int > ( n ));
for ( int i = 0 ; i < m && obstacleGrid [ i ][ 0 ] == 0 ; ++ i ) {
f [ i ][ 0 ] = 1 ;
}
for ( int j = 0 ; j < n && obstacleGrid [ 0 ][ j ] == 0 ; ++ j ) {
f [ 0 ][ j ] = 1 ;
}
for ( int i = 1 ; i < m ; ++ i ) {
for ( int j = 1 ; j < n ; ++ j ) {
if ( obstacleGrid [ i ][ j ] == 0 ) {
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ];
}
}
}
return f [ m - 1 ][ n - 1 ];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 func uniquePathsWithObstacles ( obstacleGrid [][] int ) int {
m , n := len ( obstacleGrid ), len ( obstacleGrid [ 0 ])
f := make ([][] int , m )
for i := 0 ; i < m ; i ++ {
f [ i ] = make ([] int , n )
}
for i := 0 ; i < m && obstacleGrid [ i ][ 0 ] == 0 ; i ++ {
f [ i ][ 0 ] = 1
}
for j := 0 ; j < n && obstacleGrid [ 0 ][ j ] == 0 ; j ++ {
f [ 0 ][ j ] = 1
}
for i := 1 ; i < m ; i ++ {
for j := 1 ; j < n ; j ++ {
if obstacleGrid [ i ][ j ] == 0 {
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ]
}
}
}
return f [ m - 1 ][ n - 1 ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 function uniquePathsWithObstacles ( obstacleGrid : number [][]) : number {
const m = obstacleGrid . length ;
const n = obstacleGrid [ 0 ]. length ;
const f = Array . from ({ length : m }, () => Array ( n ). fill ( 0 ));
for ( let i = 0 ; i < m ; i ++ ) {
if ( obstacleGrid [ i ][ 0 ] === 1 ) {
break ;
}
f [ i ][ 0 ] = 1 ;
}
for ( let i = 0 ; i < n ; i ++ ) {
if ( obstacleGrid [ 0 ][ i ] === 1 ) {
break ;
}
f [ 0 ][ i ] = 1 ;
}
for ( let i = 1 ; i < m ; i ++ ) {
for ( let j = 1 ; j < n ; j ++ ) {
if ( obstacleGrid [ i ][ j ] === 1 ) {
continue ;
}
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ];
}
}
return f [ m - 1 ][ n - 1 ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 impl Solution {
pub fn unique_paths_with_obstacles ( obstacle_grid : Vec < Vec < i32 >> ) -> i32 {
let m = obstacle_grid . len ();
let n = obstacle_grid [ 0 ]. len ();
let mut f = vec! [ vec! [ 0 ; n ]; m ];
for i in 0 .. n {
if obstacle_grid [ 0 ][ i ] == 1 {
break ;
}
f [ 0 ][ i ] = 1 ;
}
for i in 0 .. m {
if obstacle_grid [ i ][ 0 ] == 1 {
break ;
}
f [ i ][ 0 ] = 1 ;
}
for i in 1 .. m {
for j in 1 .. n {
if obstacle_grid [ i ][ j ] == 1 {
continue ;
}
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ];
}
}
f [ m - 1 ][ n - 1 ]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 /**
* @param {number[][]} obstacleGrid
* @return {number}
*/
var uniquePathsWithObstacles = function ( obstacleGrid ) {
const m = obstacleGrid . length ;
const n = obstacleGrid [ 0 ]. length ;
const f = Array . from ({ length : m }, () => Array ( n ). fill ( 0 ));
for ( let i = 0 ; i < m ; i ++ ) {
if ( obstacleGrid [ i ][ 0 ] === 1 ) {
break ;
}
f [ i ][ 0 ] = 1 ;
}
for ( let i = 0 ; i < n ; i ++ ) {
if ( obstacleGrid [ 0 ][ i ] === 1 ) {
break ;
}
f [ 0 ][ i ] = 1 ;
}
for ( let i = 1 ; i < m ; i ++ ) {
for ( let j = 1 ; j < n ; j ++ ) {
if ( obstacleGrid [ i ][ j ] === 1 ) {
continue ;
}
f [ i ][ j ] = f [ i - 1 ][ j ] + f [ i ][ j - 1 ];
}
}
return f [ m - 1 ][ n - 1 ];
};