1. 题目来源
链接:542. 01 矩阵
2. 题目说明
3. 题目解析
方法一:bfs变种+巧妙解法
按标签刷题,是一道求 距离场 例题。给了一个只有 0 和 1 的矩阵,求每一个 1 到离其最近的 0 的距离,其实也就是求一个 距离场,而求距离场那么 BFS
将是不二之选。
若采用暴力直接找的方法,即从每一个 0 开始遍历,不停的更新每一个 1 的距离,但是这样写下来会 TLE
了。
再改变思路,从每一个 1 开始 BFS
,找到最近的 0,结果还是 TLE
,气死人…
至此其实思路是对的,看了看大佬的想法,顿时醍醐灌顶,写法上可以进一步优化即可:
- 首先遍历一次矩阵,将值为 0 的点都存入
queue
,将值为 1 的点改为INT_MAX
- 之前像什么遍历迷宫啊,起点只有一个,而这道题所有为 0 的点都是起点,这想法
tql
- 然后开始
BFS
遍历,从queue
中取出一个数字,遍历其周围四个点,如果越界或者周围点的值小于等于当前值加 1,则直接跳过。因为周围点的距离更小的话,就没有更新的必要,否则将周围点的值更新为当前值加 1,然后把周围点的坐标加入queue
即可
参见代码如下:
// 执行用时 :136 ms, 在所有 C++ 提交中击败了41.24%的用户
// 内存消耗 :25.4 MB, 在所有 C++ 提交中击败了66.67%的用户
class Solution {
public:
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
queue<pair<int, int>> q;
int m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 0) q.push({i, j});
else matrix[i][j] = INT_MAX;
}
}
while (!q.empty()) {
auto t = q.front();
q.pop();
for (int i = 0; i < 4; ++i) {
int x = t.first + dir[i][0];
int y = t.second + dir[i][1];
if (x < 0 or x >= m or y < 0 or y >= n or
matrix[x][y] <= matrix[t.first][t.second] + 1) continue;
matrix[x][y] = matrix[t.first][t.second] + 1;
q.push({x, y});
}
}
return matrix;
}
};
方法二:dp+巧妙解法
有一说一,这个 dp
解法对于了解 bfs
的刷题手来讲大多都不是第一选择,思考起来确实有遗漏,并且还是需要知道一定的结论才能进行到常数的优化。所以在此我感觉了解即可,即知道
- 划重点:曼哈顿距离,通过【左上到右下】和【右下到左上】的两次状态转移之后,
res[i][j]
的值即为题目所求的到最近值为 0 点的距离
这个 dp
思路很值得去思考,证明。但对于初学者来讲,知道上面的结论也就差不多了,感兴趣同学可自行证明该结论或是参考题解区后的相关证明。在此由于博主能力有限,就不作深究了。
参见代码如下:
// 执行用时 :112 ms, 在所有 C++ 提交中击败了54.28%的用户
// 内存消耗 :23 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
// 初始化动态规划的数组,所有的距离值都设置为一个很大的数
vector<vector<int>> dist(m, vector<int>(n, INT_MAX / 2));
// 如果 (i, j) 的元素为 0,那么距离为 0
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 0) {
dist[i][j] = 0;
}
}
}
// 只有 水平向左移动 和 竖直向上移动,注意动态规划的计算顺序
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (i - 1 >= 0) dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1);
if (j - 1 >= 0) dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1);
}
}
// 只有 水平向右移动 和 竖直向下移动,注意动态规划的计算顺序
for (int i = m - 1; i >= 0; --i) {
for (int j = n - 1; j >= 0; --j) {
if (i + 1 < m) dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1);
if (j + 1 < n) dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1);
}
}
return dist;
}
};