image.png

状态定义:dp[i][j] 表示到达 (i,j)处的最小数字总和

问题目标:dp[n][m]

状态转移:由于每次只能往右或往下移动,那么 dp[i][j] 必然与 dp[i-1][j] (:(i-1,j)往下走一步到(i,j)) 和 dp[i][j - 1]有关,又由于题目对路径的格子加了约束(有些格子不能访问),那么有
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + arr[i][j]

状态初始化:dp[1][1] = 1,且 dp 中第一行、第一列为累加数组

时间复杂度:O(m * n)

空间复杂度:O(m * n) ——可优化

原始代码

  1. class Solution {
  2. public int minPathSum(int[][] grid) {
  3. int m = grid.length;
  4. int n = grid[0].length;
  5. int[][] dp = new int[m + 1][n + 1];
  6. //初始化dp
  7. dp[1][1] = grid[0][0];
  8. for(int i = 2; i <= n; i++){ //第一行
  9. dp[1][i] = dp[1][i-1] + grid[0][i-1];
  10. }
  11. for(int i = 2; i <= m; i++){ //第一列
  12. dp[i][1] = dp[i-1][1] + grid[i-1][0];
  13. }
  14. for(int i = 2; i <= m; i++){
  15. for(int j = 2; j <= n; j++){
  16. dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1];
  17. }
  18. }
  19. return dp[m][n];
  20. }
  21. }

如何记录最优路径?

  1. class Solution {
  2. int m, n;
  3. public int minPathSum(int[][] grid) {
  4. m = grid.length;
  5. n = grid[0].length;
  6. int[][] f = new int[m][n];
  7. int[] g = new int[m * n];
  8. for (int i = 0; i < m; i++) {
  9. for (int j = 0; j < n; j++) {
  10. if (i == 0 && j == 0) {
  11. f[i][j] = grid[i][j];
  12. } else {
  13. int top = i - 1 >= 0 ? f[i - 1][j] + grid[i][j] : Integer.MAX_VALUE;
  14. int left = j - 1 >= 0 ? f[i][j - 1] + grid[i][j] : Integer.MAX_VALUE;
  15. f[i][j] = Math.min(top, left);
  16. g[getIdx(i, j)] = top < left ? getIdx(i - 1, j) : getIdx(i, j - 1);
  17. }
  18. }
  19. }
  20. // 从「结尾」开始,在 g[] 数组中找「上一步」
  21. int idx = getIdx(m - 1, n - 1);
  22. // 逆序将路径点添加到 path 数组中
  23. int[][] path = new int[m + n][2];
  24. path[m + n - 1] = new int[]{m - 1, n - 1};
  25. for (int i = 1; i < m + n; i++) {
  26. path[m + n - 1 - i] = parseIdx(g[idx]);
  27. idx = g[idx];
  28. }
  29. // 顺序输出位置
  30. for (int i = 1; i < m + n; i++) {
  31. int x = path[i][0], y = path[i][1];
  32. System.out.print("(" + x + "," + y + ") ");
  33. }
  34. System.out.println(" ");
  35. return f[m - 1][n - 1];
  36. }
  37. int[] parseIdx(int idx) {
  38. return new int[]{idx / n, idx % n};
  39. }
  40. int getIdx(int x, int y) {
  41. return x * n + y;
  42. }
  43. }