Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 193 additions & 40 deletions solution/0000-0099/0063.Unique Paths II/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,151 @@

## 解法

### 方法一:动态规划
### 方法一:记忆化搜索

我们定义 $dp[i][j]$ 表示到达网格 $(i,j)$ 的路径数。
我们设计一个函数 $dfs(i, j)$ 表示从网格 $(i, j)$ 到网格 $(m - 1, n - 1)$ 的路径数。其中 $m$ 和 $n$ 分别是网格的行数和列数

首先初始化 $dp$ 第一列和第一行的所有值,然后遍历其它行和列,有两种情况
函数 $dfs(i, j)$ 的执行过程如下

- 若 $obstacleGrid[i][j] = 1$,说明路径数为 $0$,那么 $dp[i][j] = 0$;
- 若 ¥ obstacleGrid[i][j] = 0$,则 $dp[i][j] = dp[i - 1][j] + dp[i][j - 1]$。
- 如果 $i \ge m$ 或者 $j \ge n$,或者 $obstacleGrid[i][j] = 1$,则路径数为 $0$;
- 如果 $i = m - 1$ 且 $j = n - 1$,则路径数为 $1$;
- 否则,路径数为 $dfs(i + 1, j) + dfs(i, j + 1)$。

最后返回 $dp[m - 1][n - 1]$ 即可。
为了避免重复计算,我们可以使用记忆化搜索的方法。

时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别是网格的行数和列数。

<!-- tabs:start -->

```python
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)
```

```java
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];
}
}
```

```cpp
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size(), n = obstacleGrid[0].size();
int f[m][n];
memset(f, -1, sizeof(f));
function<int(int, int)> 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);
}
};
```

```go
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)
}
```

```ts
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);
}
```

<!-- tabs:end -->

### 方法二:动态规划

我们定义 $f[i][j]$ 表示到达网格 $(i,j)$ 的路径数。

首先初始化 $f$ 第一列和第一行的所有值,然后遍历其它行和列,有两种情况:

- 若 $obstacleGrid[i][j] = 1$,说明路径数为 $0$,那么 $f[i][j] = 0$;
- 若 $obstacleGrid[i][j] = 0$,则 $f[i][j] = f[i - 1][j] + f[i][j - 1]$。

最后返回 $f[m - 1][n - 1]$ 即可。

时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别是网格的行数和列数。

Expand All @@ -68,41 +203,41 @@
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0] * n for _ in range(m)]
f = [[0] * n for _ in range(m)]
for i in range(m):
if obstacleGrid[i][0] == 1:
break
dp[i][0] = 1
f[i][0] = 1
for j in range(n):
if obstacleGrid[0][j] == 1:
break
dp[0][j] = 1
f[0][j] = 1
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 0:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[-1][-1]
f[i][j] = f[i - 1][j] + f[i][j - 1]
return f[-1][-1]
```

```java
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length, n = obstacleGrid[0].length;
int[][] dp = new int[m][n];
int[][] f = new int[m][n];
for (int i = 0; i < m && obstacleGrid[i][0] == 0; ++i) {
dp[i][0] = 1;
f[i][0] = 1;
}
for (int j = 0; j < n && obstacleGrid[0][j] == 0; ++j) {
dp[0][j] = 1;
f[0][j] = 1;
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (obstacleGrid[i][j] == 0) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
}
return dp[m - 1][n - 1];
return f[m - 1][n - 1];
}
}
```
Expand All @@ -112,75 +247,96 @@ class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size(), n = obstacleGrid[0].size();
vector<vector<int>> dp(m, vector<int>(n));
vector<vector<int>> f(m, vector<int>(n));
for (int i = 0; i < m && obstacleGrid[i][0] == 0; ++i) {
dp[i][0] = 1;
f[i][0] = 1;
}
for (int j = 0; j < n && obstacleGrid[0][j] == 0; ++j) {
dp[0][j] = 1;
f[0][j] = 1;
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (obstacleGrid[i][j] == 0) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
}
return dp[m - 1][n - 1];
return f[m - 1][n - 1];
}
};
```

```go
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
m, n := len(obstacleGrid), len(obstacleGrid[0])
dp := make([][]int, m)
f := make([][]int, m)
for i := 0; i < m; i++ {
dp[i] = make([]int, n)
f[i] = make([]int, n)
}
for i := 0; i < m && obstacleGrid[i][0] == 0; i++ {
dp[i][0] = 1
f[i][0] = 1
}
for j := 0; j < n && obstacleGrid[0][j] == 0; j++ {
dp[0][j] = 1
f[0][j] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
if obstacleGrid[i][j] == 0 {
dp[i][j] = dp[i-1][j] + dp[i][j-1]
f[i][j] = f[i-1][j] + f[i][j-1]
}
}
}
return dp[m-1][n-1]
return f[m-1][n-1]
}
```

```ts
function uniquePathsWithObstacles(obstacleGrid: number[][]): number {
const m = obstacleGrid.length;
const n = obstacleGrid[0].length;
const dp = Array.from({ length: m }, () => new Array(n).fill(0));
const f = Array.from({ length: m }, () => Array(n).fill(0));
for (let i = 0; i < m; i++) {
if (obstacleGrid[i][0] === 1) {
break;
}
dp[i][0] = 1;
f[i][0] = 1;
}
for (let i = 0; i < n; i++) {
if (obstacleGrid[0][i] === 1) {
break;
}
dp[0][i] = 1;
f[0][i] = 1;
}
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
if (obstacleGrid[i][j] === 1) {
continue;
}
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
return dp[m - 1][n - 1];
return f[m - 1][n - 1];
}
```

```ts
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);
}
```

Expand All @@ -189,31 +345,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();
if obstacle_grid[0][0] == 1 || obstacle_grid[m - 1][n - 1] == 1 {
return 0;
}
let mut dp = vec![vec![0; n]; m];
let mut f = vec![vec![0; n]; m];
for i in 0..n {
if obstacle_grid[0][i] == 1 {
break;
}
dp[0][i] = 1;
f[0][i] = 1;
}
for i in 0..m {
if obstacle_grid[i][0] == 1 {
break;
}
dp[i][0] = 1;
f[i][0] = 1;
}
for i in 1..m {
for j in 1..n {
if obstacle_grid[i][j] == 1 {
continue;
}
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
dp[m - 1][n - 1]
f[m - 1][n - 1]
}
}
```
Expand Down
Loading