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

来源:互联网 发布:淘宝拒收怎么申请退款 编辑:程序博客网 时间:2024/05/20 22:29

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 00 1 00 0 0
Output:
0 0 00 1 00 0 0

Example 2: 
Input:

0 0 00 1 01 1 1
Output:
0 0 00 1 01 2 1

Note:

  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. 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 0dist[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((rc)2). Iterating over the entire matrix for each 1 in the matrix.

  • Space complexity: O(r \cdot c)O(rc). 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 0found 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 0s and thereby, updating the distances of all the 1s 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 0s 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(rc).
  • 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(rc). Additional O(r \cdot c)O(rc) 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][j1],dist[i1][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(rc). 2 passes of r \cdot crc each
  • Space complexity: O(r \cdot c)O(rc). 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就赋一个大的值。。




原创粉丝点击