leetcode64-最小路径和

244 阅读3分钟

题目描述

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

思考

这道理是一道典型的dp问题。 我们可以知道的是,对任意的(k, j),如果我们需要到达 (k, j)点的方式有两种 从 (k - 1, j) 或者 (k, j - 1)两个位置到达(k, j)

因此到达(k, j)的最短距离就是

min(到达(k - 1, j)的最短距离, 到达(k, j - 1)的最短距离) + (k, j)的值

我们假设到达第(k, j)的最短距离为 minDis(k, j), 点(k , j)的值为value(k, j);那么状态转移方程如下

minDis(k, j) = min(minDis(k - 1, j), minDis(k, j - 1)) + value(k, j);

小二 上代码!

矩阵设置为grid 则代码如下

function minDis(k, j) {
    if (k == 0 && j == 0) {
        return grid[0][0];
    }
    return  Math.min(minDis(k - 1, j), minDis(k, j - 1)) + grid[k][j];
}

我们是不是得考虑下边界条件的问题? 我们可以发现的是在k 或 (j)等于0的时候,k - 1或 (j - 1)是不存在的,此时到达(k, j)的最短距离就是 minDis(0, j - 1) + grid(k , j)或 minDis(k - 1, 0) + grid(k, j)因此我们的代码要添上这两条

if (k == 0) {
    return minDis(0, j - 1) + grid[0][j];
}
if (j == 0) {
    return minDis(k - 1, 0) + grid[k][0];
}

完整函数

function minDis(k, j) {
    if (k == 0 && j == 0) {
        return grid[0][0];
    }
    if (k == 0) {
        return minDis(0, j - 1) + grid[0][j];
    }
    if (j == 0) {
        return minDis(k - 1, 0) + grid[k][0];
    }

    return  Math.min(minDis(k - 1, j), minDis(k, j - 1)) + grid[k][j];
}

则针对本题,我们的完整代码如下

var minPathSum = function(grid) {
    let m = grid.length;
    let n = grid[0].length;

    function minDis(k, j) {
        if (k == 0 && j == 0) {
            return grid[0][0];
        }
        if (k == 0) {
            return minDis(0, j - 1) + grid[0][j];
        }
        if (j == 0) {
            return minDis(k - 1, 0) + grid[k][0];
        }
        return  Math.min(minDis(k - 1, j), minDis(k, j - 1)) + grid[k][j];
    }
    
    return minDis(m - 1, n - 1)
};

少年 你以为这样就完了吗?

少年,你以为这样就结束了吗?不,当你提交的时候你会很快的发现一个问题,超时了! 我们来看看为什么超时: 举个例子: 这是我们的矩阵grid

[
  [1,2,3],
  [4,5,6],
  [7,8,9]
]

我们来看下走到9的过程 第一步 判断到达 86的最短距离 第二步 到达8的方式有75, 到达6的方式有53 那么这里就是

7 -> 8 -> 9
5 -> 8 -> 9

5 -> 6 -> 9
3 -> 6 -> 9

第三步 到达7的方式有4, 到达5的方式有24, 到达3的方式有2, 这里就是

4 -> 7 -> 8 -> 9

2 -> 5 -> 8 -> 9
4 -> 5 -> 8 -> 9

2 -> 5 -> 6 -> 9
4 -> 5 -> 6 -> 9

2 -> 3 -> 6 -> 9

到达24的方式只有一个1

1 -> 4 -> 7 -> 8 -> 9

1 -> 2 -> 5 -> 8 -> 9
1 -> 4 -> 5 -> 8 -> 9

1 -> 2 -> 5 -> 6 -> 9
1 -> 4 -> 5 -> 6 -> 9

1 -> 2 -> 3 -> 6 -> 9

在这里我们可以看出什么? 到达5的最短距离被重复计算了4次,充分的浪费了时间和空间。 怎么办呢? 我们可以声明一个数组,存储到达每个节点的最短距离 每一次计算最短距离的时候,如果某个节点的最短距离已经计算出了,那么我们直接提取即可

之前代码更改如下

 var minPathSum = function(grid) {
    let m = grid.length;
    let n = grid[0].length;
        
    let minDisPoint = [];
    for(let i = 0;i < m; i++) {
        minDisPoint[i] = [];
        for (let j = 0; j <n ;j++) {
            minDisPoint[i].push(-1);
        }
    }
        
    function minDis(k, j) {
        let value = 0;
        if (k == 0 && j == 0) {

            value = grid[0][0];
        } else if (minDisPoint[k][j] != -1) {
            
            value = minDisPoint[k][j];
        } else if (k == 0) {
            
            value = minDis(0, j - 1) + grid[0][j]
        } else if (j == 0) {
            
            value = minDis(k - 1, 0) + grid[k][0];
        } else {
            
            value = Math.min(minDis(k - 1, j), minDis(k, j - 1)) + grid[k][j];
        }

        minDisPoint[k][j] = value; 

        return  value;
    }

    return minDis(m - 1, n - 1);
};

总结

列出状态转移方程

考虑边界情况

注意剪枝