题目描述
给定一个包含非负整数的 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的过程
第一步
判断到达 8、6的最短距离
第二步
到达8的方式有7、5, 到达6的方式有5、3
那么这里就是
7 -> 8 -> 9
5 -> 8 -> 9
5 -> 6 -> 9
3 -> 6 -> 9
第三步
到达7的方式有4,
到达5的方式有2、4,
到达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
到达2,4的方式只有一个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);
};
总结
列出状态转移方程
考虑边界情况
注意剪枝