leetcode 542 01矩阵(多源BFS or dp)
题目描述:
给定一个由0和1组成的矩阵,找出每个元素到最近0的距离,两个相邻元素间的距离为1.
题解:
BFS:
先考虑只有一个零的情况,那么只需要从这个零出发BFS更新距离就好。现在矩阵中有多个零,那么我们将这个多个零看作同一层(也可以理解为一个零的集合),然后以这个集合为起点同步BFS即可。举个例子,在这个例子中,有两个 0:
_ _ _ _ _ 0 _ _ _ _ 0 _ _ _ _ _
我们会把这两个 0 的位置都加入初始队列中,随后我们进行广度优先搜索,找到所有距离为 1 的 1:
_ 1 _ _ 1 0 1 _ _ 1 0 1 _ _ 1 _
接着重复步骤,直到搜索完成:
_ 1 _ _ 2 1 2 _ 2 1 2 3 1 0 1 _ ==> 1 0 1 2 ==> 1 0 1 2 _ 1 0 1 2 1 0 1 2 1 0 1 _ _ 1 _ _ 2 1 2 3 2 1 2
理解清楚BFS的层次遍历方式之后,上述多源BFS理解起来应该不难。代码如下:
class Solution { public: // bfs dp int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; typedef pair<int,int> pii; vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) { int n = matrix.size(), m = matrix[0].size(); vector<vector<int>> ans(n,vector<int>(m,INT_MAX/2)); int vis[n+1][m+1]; memset(vis,0,sizeof(vis)); queue<pii> que; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(matrix[i][j] == 0) { vis[i][j] = 1; ans[i][j] = 0; que.emplace(i,j); } } } while(!que.empty()) { pii now = que.front(); que.pop(); for(int i=0;i<4;i++) { int xx = now.first + dir[i][0]; int yy = now.second + dir[i][1]; if(xx < 0 || xx>=n || yy < 0 || yy >=m || vis[xx][yy] == 1) continue; ans[xx][yy] = min(ans[xx][yy],ans[now.first][now.second] + 1); vis[xx][yy] = 1; que.emplace(xx,yy); } } return ans; } };
DP:
定义dp[i][j]为位置i,j到最近0的距离。需要梳理清楚的是dp的递推过程,也就是建立一个有序的状态转移。这里$f[x][y]$可以由$f[x][y-1]、f[x][y+1]、f[x-1][y]、f[x+1][y]$四个状态转移而来,那么至少需要遍历两次矩阵,分别为:
-
只有 水平向左移动 和 竖直向上移动;
-
只有 水平向右移动 和 竖直向下移动。
限制好移动的方向之后,状态的转移就比较简单了,详见代码:
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; } };