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

解析1(二维数组法):假设dp[i][j]是从ij点到最右下角元素的最小路径,那么dp[j][j]=min(dp[i+1][j],dp[i][j+1])+当前元素.
时间复杂度 :O(mn)O(mn)。遍历整个矩阵恰好一次。
空间复杂度 :O(mn)O(mn)。额外的一个同大小矩阵。

public class Solution {
    public int minPathSum(int[][] grid) {
        int[][] dp = new int[grid.length][grid[0].length];
        for (int i = grid.length - 1; i >= 0; i--) {//从原来二维数组右下角更新整个数组
            for (int j = grid[0].length - 1; j >= 0; j--) {
                if(i == grid.length - 1 && j != grid[0].length - 1)//最下一行,但不是最右下角元素
                    dp[i][j] = grid[i][j] +  dp[i][j + 1];//只有一种路径方法
                else if(j == grid[0].length - 1 && i != grid.length - 1)//最右一行,但不是最右下角元素
                    dp[i][j] = grid[i][j] + dp[i + 1][j];//只有一种路径方法
                else if(j != grid[0].length - 1 && i != grid.length - 1)//其他
                    dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);
                else//最右下角元素
                    dp[i][j] = grid[i][j];
            }
        }
        return dp[0][0];
    }
}

解析2(二维数组):与上述相比区别在于,直接在愿数组上进行更新,不需要额外的空间复杂度。
时间复杂度 :O(mn)O(mn)。遍历整个矩阵恰好一次。
空间复杂度 :O(1)O(1)。不需要额外空间。

public class Solution {
    public int minPathSum(int[][] grid) {
        for (int i = grid.length - 1; i >= 0; i--) {
            for (int j = grid[0].length - 1; j >= 0; j--) {
                if(i == grid.length - 1 && j != grid[0].length - 1)
                    grid[i][j] = grid[i][j] +  grid[i][j + 1];
                else if(j == grid[0].length - 1 && i != grid.length - 1)
                    grid[i][j] = grid[i][j] + grid[i + 1][j];
                else if(j != grid[0].length - 1 && i != grid.length - 1)
                    grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j],grid[i][j + 1]);
            }
        }
        return grid[0][0];
    }
}

解析3(一维数组):假设dp[j],j的长度为列数,我们只需要更新dp[],dp[0]即为所求。
时间复杂度 :O(mn)O(mn)。遍历整个矩阵恰好一次。
空间复杂度 :O(n)O(n)。额外的一维数组,和一行大小相同。

public class Solution {
   public int minPathSum(int[][] grid) {
       int[] dp = new int[grid[0].length];
       for (int i = grid.length - 1; i >= 0; i--) {//从原来二维数组右下角更新整个数组
           for (int j = grid[0].length - 1; j >= 0; j--) {
               if(i == grid.length - 1 && j != grid[0].length - 1)//最下一行,但不是最右下角元素
                   dp[j] = grid[i][j] +  dp[j + 1];//只有一种路径方法
               else if(j == grid[0].length - 1 && i != grid.length - 1)//最右一行,但不是最右下角元素
                   dp[j] = grid[i][j] + dp[j];//也只有一种路径方法
               else if(j != grid[0].length - 1 && i != grid.length - 1)//其他
                   dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]);//
               else//最右下角元素
                   dp[j] = grid[i][j];
           }
       }
       return dp[0];
   }
}