vlambda博客
学习文章列表

动态规划<7>最小路径和(minimum-path-sum)

本节为动态规划第七题:最小路径和(minimum-path-sum),主要包括:问题描述,算法思路,复杂度分析,C++实现。

1. 问题描述

  给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
  说明:每次只能向下或者向右移动一步。


  示例:
  输入:
  [
   [1,3,1],
   [1,5,1],
   [4,2,1]
  ]
  输出: 7
  解释: 因为路径 1→3→1→1→1 的总和最小。


2. 解法

2.1 暴力法

  最简单的思路,利用递归,对于每个元素,我们都考虑两条路径,即向右或者向下(到达该元素),在这两条路径中挑选路径权值较小的一个:cost[i,j]=grid[i][j]+min(cost(i+1,j),cost(i,j+1))。C++代码如下:

//1.暴力法,时间复杂度O(2^{m*n}),空间复杂度O(m+n)->递归的深度 int calculate(vector<vector<int>>& grid, int i, int j) { if (i == grid.size() || j == grid[0].size()) return 100000; if (i == grid.size() - 1 && j == grid[0].size() - 1) return grid[i][j]; return grid[i][j] + min(calculate(grid, i + 1, j), calculate(grid, i, j + 1)); } int minPathSum(vector<vector<int>>& grid) { return calculate(grid, 0, 0); }
2.2 二维动态规划

  新建一个与原矩阵大小相同的dp数组,dp[i][j]表示从坐标(i,j)到右下角的最小路径权值,初始化右下角的dp值为对应的原矩阵值,然后填这个dp数组,对于每个元素考虑移动到向右或向下移动到该元素,得到如下递推公式,和暴力法类似:
dp[i,j]=grid[i][j]+min(dp(i+1,j),dp(i,j+1)),C++代码如下:

//2.二维动态规划,时间复杂度O(m*n),空间复杂度O(m*n) int minPathSum(vector<vector<int>>& grid) { vector<vector<int>> dp(grid.size(), vector<int>(grid[0].size())); for (int i = grid.size() - 1; i >= 0; i--) { for (int j = grid[0].size() - 1; j >= 0; j--) { //最后一行 if (i == grid.size() - 1 && j != grid[0].size() - 1) dp[i][j] = grid[i][j] + dp[i][j + 1]; //最后一列 else if (j == grid[0].size() - 1 && i != grid.size() - 1) dp[i][j] = grid[i][j] + dp[i + 1][j]; //其余 else if (j != grid[0].size() && i != grid.size() - 1) dp[i][j] = grid[i][j] + min(dp[i + 1][j], dp[i][j + 1]); //右下角 else dp[i][j] = grid[i][j]; } } return dp[0][0]; }

2.3 一维动态规划

  实际上可以用一维数组代替二维数组,dp数组的大小和行大小相同,初始化dp数组最后一个元素为右下角的元素值,然后向左更新dp数组:dp[j]=grid[i][j]+min(dp[j],dp[j+1]),C++代码如下:

//2.一维动态规划,时间复杂度O(m*n),空间复杂度O(n) int minPathSum(vector<vector<int>>& grid) { vector<int> dp(grid[0].size()); for (int i = grid.size() - 1; i >= 0; i--) { for (int j = grid[0].size() - 1; j >= 0; j--) { //最后一行 if (i == grid.size() - 1 && j != grid[0].size() - 1) dp[j] = grid[i][j] + dp[j + 1]; //最后一列 else if (j == grid[0].size() - 1 && i != grid.size() - 1) dp[j] = grid[i][j] + dp[j]; //其余 else if (j != grid[0].size() && i != grid.size() - 1) dp[j] = grid[i][j] + min(dp[j], dp[j + 1]); //右下角 else dp[j] = grid[i][j]; } } return dp[0]; }

2.4 不需要额外空间的动态规划

  自然,我们可以以用原矩阵作为dp数组,这样空间复杂度降到O(1),状态转移方程列为:grid(i,j)=grid(i,j)+min(grid(i+1,j),grid(i,j+1)),C++代码如下:

//2.不需要额外空间的动态规划,时间复杂度O(m*n),空间复杂度O(1) int minPathSum(vector<vector<int>>& grid) { for (int i = grid.size() - 1; i >= 0; i--) { for (int j = grid[0].size() - 1; j >= 0; j--) { //最后一行 if (i == grid.size() - 1 && j != grid[0].size() - 1) grid[i][j] = grid[i][j] + grid[i][j + 1]; //最后一列 else if (j == grid[0].size() - 1 && i != grid.size() - 1) grid[i][j] = grid[i][j] + grid[i + 1][j]; //其余 else if (j != grid[0].size() && i != grid.size() - 1) grid[i][j] = grid[i][j] + min(grid[i + 1][j], grid[i][j + 1]); //右下角 else grid[i][j] = grid[i][j]; } } return grid[0][0]; }

深度学习与数学

关注公号,发现更多

长按识别上方二维码关注