动态规划之最小路径和 :: labuladong的算法小抄 #995
Replies: 26 comments 5 replies
-
本题状态压缩可以直接去掉第一维 文中解法状态压缩之后代码如下: public int minPathSum2(int[][] grid) {
int[] dp = new int[grid[0].length];
dp[0] = grid[0][0];
for (int i = 0;i<grid.length;i++){
for (int j =0;j<dp.length;j++){
if (i==0 && j==0){
continue;
}
if (i-1==-1){
dp[j] = dp[j-1]+grid[i][j];
continue;
}else if (j-1==-1){
dp[j] = dp[j]+grid[i][j];
continue;
}
dp[j] = Math.min(dp[j],dp[j-1])+grid[i][j];
}
}
return dp[dp.length-1];
} |
Beta Was this translation helpful? Give feedback.
-
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int row = grid.size(), col = grid[0].size();
vector<int> dp(col);
dp[0] = grid[0][0];
for(int j = 1; j<col; ++j) dp[j] = dp[j-1] + grid[0][j];
for(int i = 1; i<row; ++i){
dp[0] = grid[i][0] + dp[0];
for(int j = 1; j<col; ++j){
dp[j] = grid[i][j] + min(dp[j], dp[j-1]);
}
}
return dp[col-1];
}
}; |
Beta Was this translation helpful? Give feedback.
-
也可以用Dijkstra算法求解: from queue import PriorityQueue
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
sol = [ [0] * len(grid[0]) for _ in grid ]
visited = set()
queue = PriorityQueue()
queue.put( (grid[0][0], (0, 0)) )
sol[0][0] = grid[0][0]
while not queue.empty():
val, node = queue.get()
if node in visited:
continue
visited.add(node)
sol[node[0]][node[1]] = val
if node[0]+1<len(grid):
down = (node[0]+1, node[1])
queue.put( (grid[down[0]][down[1]] + val, down) )
if node[1]+1<len(grid[0]):
right = (node[0], node[1]+1)
queue.put( ( grid[right[0]][right[1]] + val, right) )
return sol[-1][-1] |
Beta Was this translation helpful? Give feedback.
-
这是第一道靠自己的能力写出来的,多亏了东哥之前的解决,从之前的一道也看不懂,到后来的看了半天才能看懂,再到今天第一次自己写出来。感觉算法确实很难,但是也能从中获得乐趣。 有一个问题是,东哥后面的好多题目都是通过dptable写出来的,当我自己尝试通过dp函数写的时候,我发现我无法写出,dptable好像比dp函数要容易一些。也可能是我自己的功夫不到家,希望东哥可以多用dp函数解答题目。 |
Beta Was this translation helpful? Give feedback.
-
还有一个问题是:评论之后,就无法再次编辑修改评论 |
Beta Was this translation helpful? Give feedback.
-
Math.min( |
Beta Was this translation helpful? Give feedback.
-
@Yjppj 评论之前可以预览,想清楚再评论。你提出的这两种写法,现在看是等价的,但如果放到 for 循环里面,不见得相同,要根据具体情况来看。 |
Beta Was this translation helpful? Give feedback.
-
python版 DPtable 状态压缩 class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n=len(grid),len(grid[0])
dp=[-1 for j in range(n)]
# base case 相当于dp[0][j]=sum(grid[0][0...j-1])
dp[0]=grid[0][0]
for j in range(1,n):
dp[j]=dp[j-1]+grid[0][j]
for i in range(1,m):
# base case 相当于dp[i][0]=sum(grid[0...i-1][0])
dp[0]+=grid[i][0]
for j in range(1,n):
dp[j]=min(dp[j],dp[j-1])+grid[i][j]
return dp[n-1] 原二维: class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n=len(grid),len(grid[0])
dp=[[-1 for j in range(n)] for i in range(m)]
dp[0][0]=grid[0][0]
for j in range(1,n):
dp[0][j]=dp[0][j-1]+grid[0][j]
for i in range(1,m):
dp[i][0]=dp[i-1][0]+grid[i][0]
for i in range(1,m):
for j in range(1,n):
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]
return dp[m-1][n-1] |
Beta Was this translation helpful? Give feedback.
-
状态压缩思路每次只存上一行的dp值 CodeC++ class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
vector<int> dp(n,0);
dp[0] = grid[0][0];
for(int i=1; i<n; i++)dp[i] = dp[i-1] + grid[0][i];
for(int i=1; i<m; i++){
dp[0] = dp[0] + grid[i][0];
for(int j=1; j<n; j++){
dp[j] = grid[i][j] + min(dp[j], dp[j-1]);
}
}
return dp[n-1];
}
}; |
Beta Was this translation helpful? Give feedback.
-
大佬,返回最小路径和的所有路径有啥思路呀 |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
因为备忘录和 grid 是一样大的,且由于前进方向是向右、向下,和我们的默认遍历方向一致,所以感觉可以 public int minPathSum(int[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[i].length; j++) {
int curVal = grid[i][j];
if (i == 0 && j == 0) {
grid[i][j] = curVal;
} else if(i == 0) {
// 左边+当前
grid[i][j] = grid[i][j-1] + curVal;
} else if(j == 0) {
// 上面+当前
grid[i][j] = grid[i-1][j] + curVal;
} else {
grid[i][j] = Math.min(grid[i][j-1], grid[i-1][j]) + curVal;
}
}
}
return grid[grid.length-1][grid[0].length-1];
} 空间复杂度,因为只使用了三个变量,为 |
Beta Was this translation helpful? Give feedback.
-
Java版本 压缩空间写法 Dijkstra 算法也可以写这题。算法真的太厉害啦。 /* 迭代压缩空间写法 */
public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int[] dp = new int[n];
Arrays.fill(dp, Integer.MAX_VALUE);
// base case
dp[0] = grid[0][0];
// dp
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 第一行第一列。跳过,因为在base case 已经赋值
if (i == 0 && j == 0) continue;
// 非第一行的第一列,都只能从上面走到当前位置
if (j == 0) {
dp[j] = dp[j] + grid[i][j];
}else {
// 非第一行非第一列,可以从上面或者左边走到当前位置,取最小值
dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j];
}
}
}
return dp[n - 1];
} |
Beta Was this translation helpful? Give feedback.
-
楼主,本题自底向上的 dp 数组的迭代解法未更新到插件里。 |
Beta Was this translation helpful? Give feedback.
-
不知道为啥, 感觉降维这块 对于我来说老费劲了,搞了好几次,还是差点意思,哈哈哈哈。但是那篇降维文章还是很好的, 只是我对于新知识掌握的比较慢,大家加油! |
Beta Was this translation helpful? Give feedback.
-
我也试着压缩了一下,没想到过了哎
|
Beta Was this translation helpful? Give feedback.
-
check in |
Beta Was this translation helpful? Give feedback.
-
补充一个针对本题解法,也用到了空间压缩,当行数量小于列数量,定义一个长度等于行数量的数组横向滚动填表;当行数量大于列数量时,定义一个长度等于列数量的数组纵向填表 class Solution {
public int minPathSum(int[][] grid) {
lastRow = grid.length - 1;
lastColumn = grid[0].length - 1;
if (lastRow < lastColumn) {
return dp1(grid, 0);
}
return dp2(grid, 0);
}
int lastRow;
int lastColumn;
// 行短
public int dp1(int[][] grid, int y) {
int[] dp = new int[lastRow + 1];
int[] last = grid[lastRow];
dp[lastRow] = last[lastColumn];
// 初始化
for (int i = lastRow - 1; i >= 0; i--) {
dp[i] = dp[i + 1] + grid[i][lastColumn];
}
for (int i = lastColumn - 1; i >= 0; i--) {
for (int j = lastRow; j >= 0; j--) {
if (j == lastRow) {
dp[j] = dp[j] + grid[j][i];
continue;
}
dp[j] = Math.min(dp[j], dp[j + 1]) + grid[j][i];
}
}
return dp[y];
}
// 列短
public int dp2(int[][] grid, int y) {
int[] dp = new int[lastColumn + 1];
int[] last = grid[lastRow];
dp[lastColumn] = last[lastColumn];
// 初始化
for (int i = lastColumn - 1; i >= 0; i--) {
dp[i] = grid[lastRow][i] + dp[i + 1];
}
for (int i = lastRow - 1; i >= 0; i--) {
for (int j = lastColumn; j >= 0; j--) {
if (j == lastColumn) {
dp[j] = dp[j] + grid[i][j];
continue;
}
dp[j] = Math.min(dp[j], dp[j + 1]) + grid[i][j];
}
}
return dp[y];
}
} |
Beta Was this translation helpful? Give feedback.
-
东哥,请问对于找工作跳槽而言,写题目时如动态规划,用递归还是迭代去解题好一点?两个的思想都去学习并且每个题目都写出两个方式的解题代码,这样感觉工作量有点大,,,(感觉东哥讲题主要是在使用递归) |
Beta Was this translation helpful? Give feedback.
-
所以东哥,我可以只选其中一个方法去写代码吗😭 每一道题不想把递归还有迭代的代码都写出来,请问应对面试我选哪一个比较好😭 |
Beta Was this translation helpful? Give feedback.
-
打卡打卡 "自底向上" |
Beta Was this translation helpful? Give feedback.
-
或许可以考虑贪心 def minPathSum(self, grid: List[List[int]]) -> int:
m = len(grid)
n = len(grid[0])
list_research = [0 for _ in range(n)]
list_research[0] = grid[0][0]
#first line search
for i in range(1,n):
list_research[i] = grid[0][i]+list_research[i-1]
for j in range(1,m):
list_research[0] = list_research[0] + grid[j][0]
for i in range(1,n):
list_research[i] = min(list_research[i], list_research[i-1])+grid[j][i]
return list_research[-1] |
Beta Was this translation helpful? Give feedback.
-
打卡,终于可以快速按照模板自己写出来二维DP+降维优化了,谢谢东哥的讲解 贴一个降维优化的Java版: class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length, n = grid[0].length;
int[] dp = new int[n]; // 一次存一行的min cost
// base case
dp[0] = grid[0][0];
// base case for row 0
for (int j = 1; j < n; j ++) {
dp[j] = dp[j-1] + grid[0][j];
}
for (int i = 1; i < m; i ++) {
// 注意列需要从 0 开始,因为每次要更新掉dp[0]
for (int j = 0; j < n; j ++) {
if (j - 1 < 0) {
dp[j] = dp[j] + grid[i][j]; // 如果j-1越界,即左边没有数,只能选择从上往下走
} else {
dp[j] = Math.min(dp[j], dp[j-1]) + grid[i][j];
}
}
}
return dp[n-1];
}
} |
Beta Was this translation helpful? Give feedback.
-
Python 解法那里应该是return dp(grid, m-1, n-1),少了grid~ |
Beta Was this translation helpful? Give feedback.
-
打卡 |
Beta Was this translation helpful? Give feedback.
-
阿东的是 dp[i][j] = grid[i][j] + Math.min(dp[i-][j],dp[i][j-1]) 采用的还是如果想要到达二维数组中的一个点,只能是来自左侧或者上面一行。我这里给出按照原题目的要求,一个点可以走到相邻的右边或者下边一个点的视角:就是 memo[i][j] = grid[i][j] + Math.min(dp2(grid, i + 1, j), dp2(grid, i, j + 1)); 的解法: class Solution {
public int minPathSum(int[][] grid) {
memo = new int[grid.length][grid[0].length];
Arrays.stream(memo).forEach(subArray -> {
Arrays.fill(subArray, -1);
});
return dp(grid, 0, 0);
}
int dp(int[][] grid, int i, int j) {
// base case
// 走到 target node
if (i == grid.length - 1 && j == grid[0].length - 1) {
return grid[i][j];
}
if (i >= grid.length || j >= grid[0].length) {
return Integer.MAX_VALUE;
}
if (memo[i][j] != -1) {
return memo[i][j];
}
memo[i][j] = grid[i][j] +
Math.min(dp2(grid, i + 1, j), dp2(grid, i, j + 1));
return memo[i][j];
}
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:动态规划之最小路径和
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions