Skip to content

Commit 54d2ecd

Browse files
committed
[Feature] add for new
1 parent 8591931 commit 54d2ecd

File tree

1 file changed

+286
-0
lines changed

1 file changed

+286
-0
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
---
2+
3+
title: 算法篇专题之动态规划 dynamic-programming 22-LC64. 最小路径和 minimum-path-sum
4+
date: 2020-06-08
5+
categories: [Algorithm]
6+
tags: [algorithm, data-struct, topics, leetcode, dynamic-programming, dp, sf]
7+
published: true
8+
---
9+
10+
11+
# 数组
12+
13+
大家好,我是老马。
14+
15+
今天我们一起来学习一下不同路径
16+
17+
# LC64. 最小路径和 minimum-path-sum
18+
19+
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
20+
21+
说明:每次只能向下或者向右移动一步。
22+
23+
示例 1:
24+
25+
![1](https://assets.leetcode.com/uploads/2020/11/05/minpath.jpg)
26+
27+
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
28+
输出:7
29+
解释:因为路径 1→3→1→1→1 的总和最小。
30+
示例 2:
31+
32+
输入:grid = [[1,2,3],[4,5,6]]
33+
输出:12
34+
35+
36+
提示:
37+
38+
m == grid.length
39+
n == grid[i].length
40+
1 <= m, n <= 200
41+
0 <= grid[i][j] <= 200
42+
43+
# v1-递归
44+
45+
## 思路
46+
47+
1) 方程
48+
49+
我们每走一步只有两种选择:向下走,或者向右走。
50+
51+
因为要选择最小值路径,所以递归方程为:`Math.min(dfs(grid, x, y-1), dfs(grid, x-1, y)) + grid[x][y]`
52+
53+
解释为当前的位置,要么从左边过来,要么从上边过来。
54+
55+
2)终止条件
56+
57+
`x < 0 || y < 0`,此时返回路径为最大值。(不可达)
58+
59+
3)初始化值:
60+
61+
第一行,要加上左边的距离。
62+
63+
第一列,要加上上边的距离。
64+
65+
```java
66+
if(x == 0) {
67+
return dfs(grid, 0, y-1) + grid[0][y];
68+
}
69+
if(y == 0) {
70+
return dfs(grid, x-1, y) + grid[x][0];
71+
}
72+
```
73+
74+
## 实现
75+
76+
```java
77+
public int minPathSum(int[][] grid) {
78+
int m = grid.length;
79+
int n = grid[0].length;
80+
81+
return dfs(grid, m-1, n-1);
82+
}
83+
84+
private int dfs(int[][] grid, int x, int y) {
85+
// 此路不同
86+
if(x < 0 || y < 0) {
87+
return Integer.MAX_VALUE;
88+
}
89+
if(x == 0 && y == 0) {
90+
return grid[x][y];
91+
}
92+
if(x == 0) {
93+
return dfs(grid, 0, y-1) + grid[0][y];
94+
}
95+
if(y == 0) {
96+
return dfs(grid, x-1, y) + grid[x][0];
97+
}
98+
99+
// 两个中的最小值
100+
return Math.min(dfs(grid, x, y-1), dfs(grid, x-1, y)) + grid[x][y];
101+
}
102+
```
103+
104+
## 结果
105+
106+
超出时间限制
107+
25 / 66 个通过的测试用例
108+
109+
## 反思
110+
111+
这种递归的方法虽然直观,但是复杂度是指数的。
112+
113+
我们用 dp 来改进。
114+
115+
# v2-dp
116+
117+
## 思路
118+
119+
1) dp 数组的含义
120+
121+
dp[i][j] 代表到 (i,j) 这个位置的移动最小距离。
122+
123+
2)状态转移公式
124+
125+
其实和递归是类似的
126+
127+
```java
128+
dp[x,y] = min(dp[x][y-1], dp[x-1][y]) + grid[x][y];
129+
```
130+
131+
3) 初始化
132+
133+
```java
134+
dp[0][0] = grid[0][0];
135+
```
136+
137+
第一行、第一列初始化为前面的累加
138+
139+
4)迭代
140+
141+
两层循环,都从1开始
142+
143+
5)返回结果
144+
145+
返回 `dp[m-1][n-1]`
146+
147+
## 实现
148+
149+
```java
150+
public int minPathSum(int[][] grid) {
151+
int m = grid.length;
152+
int n = grid[0].length;
153+
154+
int[][] dp = new int[m][n];
155+
dp[0][0] = grid[0][0];
156+
for(int i = 1; i < m; i++) {
157+
dp[i][0] = dp[i-1][0] + grid[i][0];
158+
}
159+
for(int i = 1; i < n; i++) {
160+
dp[0][i] = dp[0][i-1] + grid[0][i];
161+
}
162+
163+
// 迭代
164+
for(int i = 1; i < m; i++) {
165+
for(int j = 1; j < n; j++) {
166+
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
167+
}
168+
}
169+
170+
return dp[m-1][n-1];
171+
}
172+
```
173+
174+
## 效果
175+
176+
4ms 击败 12.63%
177+
178+
## 复杂度
179+
180+
TC: O(m*n)
181+
182+
## 反思
183+
184+
dp 解法还是很清晰的。
185+
186+
还能更快吗?
187+
188+
# v3-空间压缩
189+
190+
## 滚动数组
191+
192+
只需要记住上一行的数据即可,二维 dp 可以压缩成一维 dp[n]
193+
194+
```java
195+
public int minPathSum(int[][] grid) {
196+
int m = grid.length;
197+
int n = grid[0].length;
198+
199+
int[] dp = new int[n];
200+
dp[0] = grid[0][0];
201+
202+
// 初始化第一行
203+
for (int j = 1; j < n; j++) {
204+
dp[j] = dp[j - 1] + grid[0][j];
205+
}
206+
207+
// 迭代
208+
for (int i = 1; i < m; i++) {
209+
dp[0] += grid[i][0]; // 更新第一列
210+
for (int j = 1; j < n; j++) {
211+
dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
212+
}
213+
}
214+
215+
return dp[n - 1];
216+
}
217+
```
218+
219+
### 效果
220+
221+
2ms 击败 95.85%
222+
223+
### 复杂度
224+
225+
时间复杂度:O(m·n)
226+
227+
空间复杂度:O(n)
228+
229+
## 原地修改 grid
230+
231+
如果允许修改输入数组,可以直接在 grid 上做累加,避免额外空间:
232+
233+
```java
234+
public int minPathSum(int[][] grid) {
235+
int m = grid.length;
236+
int n = grid[0].length;
237+
238+
for (int i = 1; i < m; i++) {
239+
grid[i][0] += grid[i - 1][0];
240+
}
241+
for (int j = 1; j < n; j++) {
242+
grid[0][j] += grid[0][j - 1];
243+
}
244+
245+
for (int i = 1; i < m; i++) {
246+
for (int j = 1; j < n; j++) {
247+
grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
248+
}
249+
}
250+
251+
return grid[m - 1][n - 1];
252+
}
253+
```
254+
255+
### 效果
256+
257+
2ms 击败 95.85%
258+
259+
### 复杂度
260+
261+
时间复杂度:O(m·n)
262+
263+
空间复杂度:O(1)
264+
265+
# 开源项目
266+
267+
为方便大家学习,所有相关文档和代码均已开源。
268+
269+
[leetcode-visual 资源可视化](https://houbb.github.io/leetcode-notes/leetcode/visible/index.html)
270+
271+
[leetcode 算法实现源码](https://github.com/houbb/leetcode)
272+
273+
[leetcode 刷题学习笔记](https://github.com/houbb/leetcode-notes)
274+
275+
[老马技术博客](https://github.com/houbb/lmxxf-it)
276+
277+
[老马主站](https://houbb.github.io/)
278+
279+
# 小结
280+
281+
希望本文对你有帮助,如果有其他想法的话,也可以评论区和大家分享哦。
282+
283+
各位极客的点赞收藏转发,是老马持续写作的最大动力!
284+
285+
下一节我们将讲解力扣经典,感兴趣的小伙伴可以关注一波,精彩内容,不容错过。
286+

0 commit comments

Comments
 (0)