前言
一、分析题目
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
提示:
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 200
- 0 <= grid[i][j]<= 100
拿到这个题,你该如何分析呢?怎么就知道是属于动态规划的题目呢?
我觉得你如果做的多了,可能一看就看出来了,或者你试着其他的思路是否可以解决这种问题,对于一个题目是否是动态规划的问题,你可以从以下几个方面来看。
动态规划满足三个性质:最优子结构、无后效性和大量重复子问题。
关于这个三个性质的解释,可以参考知乎的这篇文章:怎样学好动态规划?
二、解题思路
1.找出DP状态
此题需要求出从左上角出发,到达坐标(m,n)
的路径数字和最小值。因此不难想到,子问题就是从左上角出发,到达坐标(i,j)
的路径数字和最小值。
令 f[i][j]
表示从左上角到坐标(i,j)
的路径数字和最小值,原问题即可被划分为多个求最优值的子问题,且由于每次只能向下或向右移动一步,因此 f[i][j]
的取值由f[i-1][j]
和f[i][j-1]
的值决定,即符合「最优子结构原则」。
进一步验证,可以发现, f[i][j]
的取值与f[i-1][j]
和f[i][j-1]
所对应的具体路径无关,因此符合「无后效性」。
2.找出DP状态转移方程
确定完「DP 状态」后,继续确定「DP 转移方程」。
这里需要注意边界问题,比如第一行和第一列,只能向下或者向右运动。
3.代码部分
1.Java版本
class Solution {
public int minPathSum(int[][] grid) {
//先判断数据是否为空
if(grid==null||grid.length==0||grid[0].length==0){
return 0;
}
int rows = grid.length;//行
int column = grid[0].length;//列
int [][]dp = new int[rows][column];//dp数组
dp[0][0] = grid[0][0];//左上角元素值
for(int i=0;i<rows;i++){
for(int j=0;j<column;j++){
if(i==0&&j==0){
continue;
}
else if(i==0&&j!=0){//第一行的元素
dp[0][j] = dp[0][j-1]+grid[0][j];
}
else if(i!=0&&j==0){//第一列的元素
dp[i][0] = dp[i-1][0]+grid[i][0];
}
else{
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
}
return dp[rows-1][column-1];
}
}
2.C++版本
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
//判断是否为空
if (grid.size() == 0 || grid[0].size() == 0) {
return 0;
}
int rows = grid.size(), columns = grid[0].size();
auto dp = vector < vector <int> > (rows, vector <int> (columns));
dp[0][0] = grid[0][0];//初值为左上角
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if(i==0&&j==0){
continue;
}
else if(i==0&&j!=0){
dp[0][j] = dp[0][j-1]+grid[0][j];
}
else if(i!=0&&j==0){
dp[i][0] = dp[i-1][0]+grid[i][0];
}
else{
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];//状态转移方程
}
}
}
return dp[rows - 1][columns - 1];
}
};
3.Python3版本
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
#判断list是否为空
if not grid or not grid[0]:
return 0
rows, columns = len(grid), len(grid[0])
dp = [[0] * columns for _ in range(rows)]
dp[0][0] = grid[0][0]
for i in range(0, rows):
for j in range(0, columns):
if i==0 and j==0:
continue
elif i==0 and j!=0:
dp[0][j] = dp[0][j - 1] + grid[0][j]
elif i!=0 and j==0:
dp[i][0] = dp[i - 1][0] + grid[i][0]
else:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
return dp[rows - 1][columns - 1]
三、总结
提示:这个题在动态规划里面算一个中等难度的题目,感觉这块内容确实比较让头头大,只能多做题,多总结了。
Java、C++和Python的运行时间和内存消耗的对比?Java居然用这么多?
记录时间:2020年11月19日