给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每个格子是 mat 中对应位置元素到最近的 0 的距离。算法
两个相邻元素间的距离为 1 。数组
动态规划只能应用于有最优 子结构
的问题。最优子结构的意思是局部最优解能决定全局最优解
(对有些问题这个要求并不能彻底知足,故有时须要引入必定的近似)。markdown
简单地说,问题可以分解成子问题来解决
。oop
通俗一点来说,动态规划和其它遍历算法(如深/广度优先搜索)都是将原问题拆成多个子问题而后求解
,他们之间最本质的区别是,动态规划保存子问题的解,避免重复计算
。spa
解决动态规划问题的关键是找到状态转移方程
,这样咱们能够通计算和储存子问题的解来求解最终问题
。code
同时,咱们也能够对动态规划进行空间压缩
,起到节省空间消耗的效果。orm
在一些状况下,动态规划能够当作是带有状态记录(memoization)的优先搜索
。leetcode
动态规划是自下而上的
,即先解决子问题,再解决父问题;get
而用带有状态记录的优先搜索
是自上而下
的,即从父问题搜索到子问题,若重复搜索到同一个子问题则进行状态记录,防止重复计算。it
若是题目需求的是最终状态,那么使用动态搜索比较方便;
若是题目须要输出全部的路径,那么使用带有状态记录的优先搜索会比较方便。
一种办法是使用一个 dp 数组作 memoization,使得广
度优先搜索不会重复遍历相同位置;另外一种更简单的方法是,咱们从左上到右下进行一次动态搜
索,再从右下到左上进行一次动态搜索。两次动态搜索便可完成四个方向上的查找。
/**
* @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;
};
复制代码