dfs-洪水填充技巧

之前并查集也可以写

问题 1

问题2:

cpp 复制代码
class Solution {
public:
    // 深度优先搜索:把与 (i, j) 相连的整座岛屿染成编号 id
    void dfs(vector<vector<int>>& grid,int n,int m,int i,int j,int id){
        // 越界 或 不是未访问的陆地(1) 就返回
        if(i<0 || i == n || j<0 || j == m || grid[i][j] != 1) return;
        grid[i][j] = id;          // 染色,同时避免重复访问
        dfs(grid,n,m,i-1,j,id);   // 上
        dfs(grid,n,m,i+1,j,id);   // 下
        dfs(grid,n,m,i,j+1,id);   // 右
        dfs(grid,n,m,i,j-1,id);   // 左
    }

    int largestIsland(vector<vector<int>>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        int id = 2;               // 岛屿编号从 2 开始(0 是海,1 是未染色陆地)

        // 1. 给每座岛屿染色,编号依次为 2, 3, 4, ...
        for(int i = 0;i<n;i++){
            for(int j = 0;j<m;j++){
                if(grid[i][j] == 1) dfs(grid,n,m,i,j,id++);
            }
        }

        // 2. 统计每座岛屿的面积,sizes[k] 表示编号为 k 的岛屿面积
        int ans = 0;
        vector<int> sizes(id,0);
        for(int i =0 ;i<n;i++){
            for(int j = 0;j<m;j++){
                if(grid[i][j]>1) ans = max(ans,++sizes[grid[i][j]]);
            }
        }

        // 3. 枚举每个 0 格子,尝试把它变成 1,合并上下左右不同的岛屿
        vector<bool> visited(id);       // 标记本轮已经合并过的岛屿编号,防止重复加
        int up,down,left,right,merge;
        for(int i = 0;i<n;i++){
            for(int j = 0;j<m;j++){
                if(grid[i][j] == 0){
                    // 取四个方向的格子编号,越界或海则记为 0
                    up    = i>0     ? grid[i-1][j] : 0;
                    down  = i+1<n   ? grid[i+1][j] : 0;
                    left  = j>0     ? grid[i][j-1] : 0;
                    right = j+1<m   ? grid[i][j+1] : 0;

                    // 当前 0 变成 1 后,面积至少为 1
                    visited[up] = true;
                    merge = sizes[up] + 1;

                    // 下面三个方向:只有没被本轮合并过才加面积
                    if(!visited[down]){
                        merge += sizes[down];
                        visited[down] = true;
                    }
                    if(!visited[left]){
                        merge += sizes[left];
                        visited[left] = true;
                    }
                    if(!visited[right]){
                        merge += sizes[right];
                        visited[right] = true;
                    }

                    ans = max(ans,merge);   // 更新答案

                    // 还原 visited,供下一个 0 格子使用
                    visited[up] = false;
                    visited[down] = false;
                    visited[left] = false;
                    visited[right] = false;
                }
            }
        }
        return ans;
    }
};

问题3

cpp 复制代码
class Solution {
public:
    int n, m;                        // 行数、列数
    vector<vector<int>> grid;        // 成员变量,方便 dfs / worth 直接访问

    // 从 (i,j) 出发,把与它相连的、值为 1 的砖块全部标记为 2(稳定砖块)
    // 返回本次 dfs 访问到的砖块数量
    int dfs(int i, int j){
        // 越界 或 不是值为 1 的砖块,直接返回 0
        if(i<0 || i==n || j<0 || j==m || grid[i][j] != 1) return 0;
        grid[i][j] = 2;              // 标记为"稳定/已访问"
        // 当前这块 + 上下左右四个方向递归
        return 1 + dfs(i+1,j) + dfs(i-1,j) + dfs(i,j+1) + dfs(i,j-1);
    }

    // 判断 (i,j) 这块砖是否"值得"做一次 dfs
    // 条件:它是砖(值为1),并且能通过顶行或某个相邻稳定砖(值为2)与顶部连通
    bool worth(int i,int j){
        return grid[i][j] == 1 && ( i == 0                              // 在第一行,直接连顶
                                    || (i>0   && grid[i-1][j] == 2)     // 上方是稳定砖
                                    || (i+1<n && grid[i+1][j] == 2)     // 下方是稳定砖
                                    || (j>0   && grid[i][j-1] == 2)     // 左方是稳定砖
                                    || (j+1<m && grid[i][j+1] == 2));   // 右方是稳定砖
    }

    vector<int> hitBricks(vector<vector<int>>& g, vector<vector<int>>& h) {
        grid = g;                    // 拷贝一份,避免修改原矩阵
        n = g.size();
        m = g[0].size();
        int h_size = h.size();
        vector<int> ans(h_size);     // 存储每次敲击后掉落的砖块数

        // 只有一行时,敲掉第一行任意砖都会导致该行砖掉落?
        // 这里直接返回全 0,属于题目边界处理(一行时无"上方连接"概念)
        if(n == 1) return ans;

        // 第一步:先把所有要敲的砖从 grid 中"敲掉"(值减 1,1->0)
        for(auto hit : h){
            grid[hit[0]][hit[1]]--;
        }

        // 第二步:从顶行开始 dfs,标记所有与顶部连通的稳定砖块为 2
        for(int i = 0;i<m;i++){
            dfs(0,i);
        }

        // 第三步:逆序还原敲击,判断每块恢复的砖是否会带来新的掉落
        for(int i = h_size-1,row,col;i>=0;i--){
            row = h[i][0];
            col = h[i][1];
            grid[row][col]++;        // 把这块砖恢复(0->1)
            if(worth(row,col)){      // 如果它能和顶部连通
                ans[i] = dfs(row,col)-1;   // 新增的稳定砖数,减去自己
            }
        }
        return ans;
    }
};

以上思路和代码来自B站左程云

相关推荐
小静AI工程实验室1 小时前
RSA 算法详解:从模幂原理到 OAEP 加密、PSS 签名与 Python 实验
python·算法
xzal121 小时前
GESP Python笔记
笔记·python·算法
wzdark1 小时前
算法中的记忆化思想与重复子问题优化4
算法
Discipline10292 小时前
流水线运行是否正随时间变得更加集中?
算法
周玉奎先生2 小时前
CGM系列水泥基灌浆料型号参数解析及安庆重点工程应用匹配
经验分享·笔记·零售
海海不掉头发2 小时前
软件项目管理学习笔记-从软件危机到敏捷与DevOps
笔记·学习·devops
lzhdim2 小时前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
weixin_307779132 小时前
C++代码实现MATLAB中的pade函数功能
开发语言·c++·算法·matlab
smj2302_796826522 小时前
解决leetcode第4064题至多一次取反能被k整除的最长子数组II
python·算法·leetcode