天天看点

[Mbfs] lc542. 01 矩阵(bfs最短路+动态规划+巧妙解法)

文章目录

    • 1. 题目来源
    • 2. 题目说明
    • 3. 题目解析
      • 方法一:bfs变种+巧妙解法
      • 方法二:dp+巧妙解法

1. 题目来源

链接:542. 01 矩阵

2. 题目说明

[Mbfs] lc542. 01 矩阵(bfs最短路+动态规划+巧妙解法)

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+巧妙解法

LINK:思路来自官方题解

有一说一,这个

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