- 最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
1.暴力递推法
从上到右下递推
class Solution {
public int minPathSum(int[][] grid) {
//需要多加几个参数
return find(grid, 0, 0);
}
int find(int[][] grid, int i, int j){
//如果超出边界
if(i == grid.length || j == grid[0].length) return Integer.MAX_VALUE;//递推边界
if(i == grid.length - 1 && j == grid[0].length - 1) return grid[i][j];//到达正确的位置
return grid[i][j] + Math.min(find(grid, i + 1, j), find(grid, i, j + 1));
}
}
由下至上
class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length, n = grid[0].length;
//需要多加几个参数
return find(grid, m - 1, n - 1);
}
int find(int[][] grid, int i, int j){
//如果超出边界
if(i < 0 || j < 0)return Integer.MAX_VALUE;
if(i == 0 && j == 0)return grid[i][j];
return grid[i][j] + Math.min(find(grid, i - 1, j), find(grid, i, j - 1));
}
}
2.动态规划,二维数组
class Solution {
public int minPathSum(int[][] grid) {
//由下至上
int m = grid.length, n = grid[0].length;
int dp[][] = new int[m][n];
//dp[m - 1][n - 1] = grid[m - 1][n - 1];可以放在循环里赋值
for(int i = m - 1; i >= 0; i--){
for(int j = n - 1; j >= 0; j--){//分类讨论满足了从特殊到一般的if判断
if(i == m - 1 && j == n - 1) dp[i][j] = grid[i][j];
else if(i == m - 1) dp[i][j] = dp[i][j + 1] + grid[i][j];
else if(j == n - 1) dp[i][j] = dp[i + 1][j] + grid[i][j];
else dp[i][j] = Math.min(dp[i + 1][j], dp[i][j + 1]) + grid[i][j];
}
}
return dp[0][0];
}
}
甚至可以分开写
class Solution {
public int minPathSum(int[][] grid) {
//由下至上
int m = grid.length, n = grid[0].length;
int dp[][] = new int[m][n];
dp[m - 1][n - 1] = grid[m - 1][n - 1];
for(int i = m - 2; i >= 0; i--) dp[i][n - 1] = dp[i + 1][n - 1] + grid[i][n - 1];//先处理好边界
for(int j = n - 2; j >= 0; j--) dp[m - 1][j] = dp[m - 1][j + 1] + grid[m - 1][j];
for(int i = m - 2; i >= 0; i--) {
for (int j = n - 2; j >= 0; j--) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i][j + 1]) + grid[i][j];
}
}
return dp[0][0];
}
}
3.从上到下
class Solution {
public int minPathSum(int[][] grid) {
//由下至上
int m = grid.length, n = grid[0].length;
int dp[][] = new int[m][n];
for(int i = 0; i < m ; i++){
for(int j = 0; j < n; j++){
if(i == 0 && j == 0) dp[i][j] = grid[i][j];
else if(i == 0) dp[i][j] = grid[i][j] + dp[i][j - 1];
else if(j == 0) dp[i][j] = grid[i][j] +dp[i - 1][j];
else dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
}
4.压缩至一维dp
从上到下判断:
class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length, n = grid[0].length;
int dp[] = new int[n];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(i == 0 && j == 0) dp[j] = grid[i][j];
else if(i == 0) dp[j] = dp[j - 1] + grid[i][j];
else if(j == 0) dp[j] = dp[j] + grid[i][j];
else dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
}
}
return dp[n - 1];
}
}
从下到上
class Solution {
public int minPathSum(int[][] grid) {
//由下至上
//
int m = grid.length, n = grid[0].length;
int dp[] = new int[n];//变换为一维数组,每一行遍历的时候存储上一行
//每一次数据更新的时候一般是需要其其下方的和其右边的数据,只要保证这些已经被更新即可
for(int i = m - 1; i >= 0; i--){
for(int j = n - 1; j >= 0; j--){
if(i == m - 1 && j == n - 1) dp[j] = grid[i][j];
else if(i == m - 1) dp[j] = dp[j + 1] + grid[i][j];
else if(j == n - 1) dp[j] = dp[j] + grid[i][j];//不同之处,dp[j]是每一行开始循环的时候第一个更新的
else dp[j] = Math.min(dp[j + 1], dp[j]) + grid[i][j];
}
}
return dp[0];
}
}