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;
}
};