542. 01 矩阵
题目:
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
题解
动态规划(Dynamic Programming, DP)
-
动态规划只能
应用于有最优 子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似)。 -
简单地说,
问题能够分解成子问题来解决。 -
通俗一点来讲,动态规划和其它遍历算法(如深/广度优先搜索)都是将
原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存子问题的解,避免重复计算。 -
解决动态规划问题的关键是找到
状态转移方程,这样我们可以通计算和储存子问题的解来求解最终问题。 -
同时,我们也可以对动态规划进行
空间压缩,起到节省空间消耗的效果。 -
在一些情况下,动态规划可以看成是
带有状态记录(memoization)的优先搜索。 -
动态规划是自下而上的,即先解决子问题,再解决父问题; -
而用带有
状态记录的优先搜索是自上而下的,即从父问题搜索到子问题,若重复搜索到同一个子问题则进行状态记录,防止重复计算。 -
如果题目需求的是最终状态,那么使用动态搜索比较方便;
-
如果题目需要输出所有的路径,那么使用带有状态记录的优先搜索会比较方便。
回到本题目
一种办法是使用一个 dp 数组做 memoization,使得广
度优先搜索不会重复遍历相同位置;另一种更简单的方法是,我们从左上到右下进行一次动态搜
索,再从右下到左上进行一次动态搜索。两次动态搜索即可完成四个方向上的查找。
coding
/**
* @param {number[][]} mat
* @return {number[][]}
*/
var updateMatrix = function(mat) {
if(!mat.length) return {};
let n = mat.length, m = mat[0].length;
const dp = Array.from({length: n}, ()=> new Array(m).fill(m+n));
for(let i = 0; i < n; i++) {
for(let j = 0; j < m; j++) {
if(mat[i][j] === 0) {
dp[i][j] = 0
}else {
if(j > 0)
dp[i][j] = Math.min(dp[i][j], dp[i][j-1] + 1);
if(i > 0)
dp[i][j] = Math.min(dp[i][j], dp[i-1][j] + 1)
}
}
}
for(let i = n-1; i >= 0; i--) {
for(let j = m-1; j >= 0; j--) {
if(mat[i][j]) {
if(j < m-1)
dp[i][j] = Math.min(dp[i][j], dp[i][j+1] + 1);
if(i < n-1)
dp[i][j] = Math.min(dp[i][j], dp[i+1][j] + 1)
}
}
}
return dp;
};