您的位置:首页 > 其它

542. 01 Matrix(深搜/广搜/DP的题目)

2017-08-21 23:20 513 查看
Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell.
The distance between two adjacent cells is 1.

Example 1: 

Input:
0 0 0
0 1 0
0 0 0

Output:
0 0 0
0 1 0
0 0 0


Example 2: 

Input:
0 0 0
0 1 0
1 1 1

Output:
0 0 0
0 1 0
1 2 1


Note:

The number of elements of the given matrix will not exceed 10,000.
There are at least one 0 in the given matrix.
The cells are adjacent in only four directions: up, down, left and right.
解答(摘自Solution):


Approach #1 Brute force [Time Limit Exceeded]

Intuition

Do what the question says.

Algorithm
Initialize 
dist[i][j]=INT_MAX
 for all 
{i,j}
 cells.
Iterate over the matrix.
If cell is 
0
dist[i][j]=0
,
Else, for each 
1
 cell,
Iterate over the entire matrix
If the cell is 
0
, calculate its distance from current cell as 
abs(k-i)+abs(l-j)
.
If the distance is smaller than the current distance, update it.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
int rows = matrix.size();
if (rows == 0)
return matrix;
int cols = matrix[0].size();
vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] == 0)
dist[i][j] = 0;
else {
for (int k = 0; k < rows; k++)
for (int l = 0; l < cols; l++)
if (matrix[k][l] == 0) {
int dist_01 = abs(k - i) + abs(l - j);
dist[i][j] = min(dist[i][j], abs(k - i) + abs(l - j));
}
}
}
}
return dist;
}


Complexity Analysis

Time complexity: O((r
\cdot c)^2)O((r⋅c)​2​​).
Iterating over the entire matrix for each 
1
 in the matrix.

Space complexity: O(r
\cdot c)O(r⋅c).
No extra space required than the 
vector<vector<int> > dist



Approach #2 Using BFS [Accepted]

Intuition

A better brute force: Looking over the entire matrix appears wasteful and hence, we can use Breadth First Search(BFS) to limit the search to the nearest 
0
found
for each 
1
. As soon as a 
0
 appears
during the BFS, we know that the 
0
 is nearest, and hence, we move to the next 
1
.

Think again: But, in this approach, we will only be able to update the distance of one 
1
 using
one BFS, which could in fact, result in slightly higher complexity than the Approach #1 brute force. But hey,this could be optimised if we start the BFS from 
0
s
and thereby, updating the distances of all the 
1
s in the path.

Algorithm
For our BFS routine, we keep a queue, 
q
 to maintain the queue of cells to
be examined next.
We start by adding all the cells with 
0
s to 
q
.
Intially, distance for each 
0
 cell is 
0
 and
distance for each 
1
 is 
INT_MAX
,
which is updated during the BFS.
Pop the cell from queue, and examine its neighbours. If the new calculated distance for neighbour 
{i,j}
 is
smaller, we add 
{i,j}
 to 
q
 and
update 
dist[i][j]
.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
int rows = matrix.size();
if (rows == 0)
return matrix;
int cols = matrix[0].size();
vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX));
queue<pair<int, int> > q;
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
if (matrix[i][j] == 0) {
dist[i][j] = 0;
q.push({ i, j }); //Put all 0s in the queue.
}

int dir[4][2] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
while (!q.empty()) {
pair<int, int> curr = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int new_r = curr.first + dir[i][0], new_c = curr.second + dir[i][1];
if (new_r >= 0 && new_c >= 0 && new_r < rows && new_c < cols) {
if (dist[new_r][new_c] > dist[curr.first][curr.second] + 1) {
dist[new_r][new_c] = dist[curr.first][curr.second] + 1;
q.push({ new_r, new_c });
}
}
}
}
return dist;
}


Complexity analysis
Time complexity: O(r
\cdot c)O(r⋅c).

Since, the new cells are added to the queue only if their current distance is greater than the calculated distance, cells are not likely to be added multiple times.

Space complexity: O(r
\cdot c)O(r⋅c).
Additional O(r
\cdot c)O(r⋅c) for
queue than in Approach #1


Approach #3 DP Approach [Accepted]

Intuition

The distance of a cell from 
0
 can be calculated if we know the nearest distance for all
the neighbours, in which case the distance is minimum distance of any neightbour + 1. And, instantly, the word come to mind DP!!

For each 
1
, the minimum path to 
0
 can
be in any direction. So, we need to check all the 4 direction. In one iteration from top to bottom, we can check left and top directions, and we need another iteration from bottom to top to check for right and bottom direction.

Algorithm
Iterate the matrix from top to bottom-left to right:
Update \text{dist}[i][j]=\min(\text{dist}[i][j],\min(\text{dist}[i][j-1],\text{dist}[i-1][j])+1)dist[i][j]=min(dist[i][j],min(dist[i][j−1],dist[i−1][j])+1) i.e.,
minimum of the current dist and distance from top or left neighbour +1, that would have been already calculated previously in the current iteration.
Now, we need to do the back iteration in the similar manner: from bottom to top-right to left:
Update \text{dist}[i][j]=\min(\text{dist}[i][j],\min(\text{dist}[i][j+1],\text{dist}[i+1][j])+1)dist[i][j]=min(dist[i][j],min(dist[i][j+1],dist[i+1][j])+1) i.e.
minimum of current dist and distances calculated from bottom and right neighbours, that would be already available in current iteration.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
int rows = matrix.size();
if (rows == 0)
return matrix;
int cols = matrix[0].size();
vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX - 100000));

//First pass: check for left and top
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] == 0)
dist[i][j] = 0;
else {
if (i > 0)
dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1);
if (j > 0)
dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1);
}
}
}

//Second pass: check for bottom and right
for (int i = rows - 1; i >= 0; i--) {
for (int j = cols - 1; j >= 0; j--) {
if (i < rows - 1)
dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1);
if (j < cols - 1)
dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1);
}
}

return dist;
}


Complexity analysis
Time complexity: O(r
\cdot c)O(r⋅c).
2 passes of r
\cdot cr⋅c each
Space complexity: O(r
\cdot c)O(r⋅c).
No additional space required than 
dist vector<vector<int> >


注:
暴力法很好懂,DFS个人不感兴趣。。。DP的想法是:对于某一个位置,它到最近的0的距离=它的邻居中离这个0最近的那个+1(如果邻居有0那么直接赋0就好了)。注意第一趟要检查的是往上和往左,第二趟是下、右,不能混起来。。
其实我比较关心的是DFS怎么做(来自Discuss,Java版):
public class Solution {
public int[][] updateMatrix(int[][] matrix) {
if(matrix.length==0) return matrix;

for(int i = 0; i<matrix.length; i++)
for(int j = 0; j<matrix[0].length; j++)
if(matrix[i][j]==1&&!hasNeiberZero(i, j,matrix))
matrix[i][j] = matrix.length+matrix[0].length+1;

for(int i = 0; i<matrix.length; i++)
for(int j = 0; j<matrix[0].length; j++)
if(matrix[i][j]==1)
dfs(matrix, i, j, -1);

return matrix;
}
private void dfs(int[][] matrix, int x, int y, int val){
if(x<0||y<0||y>=matrix[0].length||x>=matrix.length||matrix[x][y]<=val)
return;

if(val>0) matrix[x][y] = val;

dfs(matrix, x+1, y, matrix[x][y]+1);
dfs(matrix, x-1, y, matrix[x][y]+1);
dfs(matrix, x, y+1, matrix[x][y]+1);
dfs(matrix, x, y-1, matrix[x][y]+1);

}
private boolean hasNeiberZero(int x, int y, int[][] matrix){
if(x>0&&matrix[x-1][y]==0) return true;
if(x<matrix.length-1&&matrix[x+1][y]==0) return true;
if(y>0&&matrix[x][y-1]==0) return true;
if(y<matrix[0].length-1&&matrix[x][y+1]==0) return true;

return false;
}
}

这个版本要清晰一点:
public class Solution {

int currMin = 12000;    // The number of elements of the given matrix will not exceed 10,000, so we can use 12,000 (or anything >10,000) throughout the program as MAX value

public int[][] updateMatrix(int[][] matrix) {
if (matrix.length == 0 || matrix[0].length == 0) {
return matrix;
}

for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 1) {
currMin = 12000;
int minDist = dfs(matrix, i, j, 0);
if (minDist < 12000)
matrix[i][j] = minDist;
}
}
}

return matrix;
}

private int dfs(int[][] matrix, int i, int j, int steps) {
if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0].length ) {
return 12000;
}
if (matrix[i][j] != 1 || steps > currMin) {
return matrix[i][j];
}
matrix[i][j] ^= 12000;
int down =    dfs(matrix, i+1, j, steps + 1);
int up =  dfs(matrix, i-1, j, steps + 1);
int left =  dfs(matrix, i, j-1, steps + 1);
int right = dfs(matrix, i, j+1, steps + 1);
matrix[i][j] ^= 12000;

int minVal = Math.min(left, Math.min(right, Math.min(up, down))) + 1;
currMin = minVal;

return minVal;
}
}
15 days ago reply quote


另一个版本的DFS:
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
for(int row=0;row<matrix.size();row++){
for(int col=0;col<matrix[0].size();col++){
if(matrix[row][col]==1&&no_adjacent_zero(matrix,row,col))
matrix[row][col]=10000;
}
}
for(int row=0;row<matrix.size();row++){
for(int col=0;col<matrix[0].size();col++){
dfs(matrix,row,col);
}
}
return matrix;
}
bool no_adjacent_zero(vector<vector<int>>& matrix,int row,int col){
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
for(int k=0;k<4;k++){
int new_row=row+next[k][0],new_col=col+next[k][1];
if(new_row<0||new_row>=matrix.size()||new_col<0||new_col>=matrix[0].size())
continue;
if(matrix[new_row][new_col]==0)
return false;
}
return true;
}
void dfs(vector<vector<int>>& matrix,int row,int col){
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
for(int k=0;k<4;k++){
int new_row=row+next[k][0],new_col=col+next[k][1];
if(new_row<0||new_row>=matrix.size()||new_col<0||new_col>=matrix[0].size())
continue;
if(matrix[new_row][new_col]>matrix[row][col]+1){
matrix[new_row][new_col]=matrix[row][col]+1;
dfs(matrix,new_row,new_col);
}
}
}
};
所以这里有一个重点就是周围没有0的点要用一个比较大的值覆盖。上一个DFS的解法是只要有1就赋一个大的值。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: