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站左程云

相关推荐
JasmineX-113 小时前
数据结构(笔记)——顺序栈
c语言·数据结构·笔记
坤坤子吖13 小时前
Linux 线程同步与互斥——互斥锁
linux·服务器·c++·笔记·学习
撩得Android一次心动13 小时前
Jetpack Compose 知识点整理3【个人用】
android·笔记·学习·kotlin·android compose
威嵌神州14 小时前
国产化加固计算机项目选型笔记|板卡、整机、平板与定制模块评估要点
笔记·fpga开发·电脑
阿梦Anmory14 小时前
规划领域定义语言(PDDL)学习笔记
笔记·学习
开开心心就好14 小时前
白天晚上自动调蓝光,护眼工具亮度可调
服务器·开发语言·算法·pdf·ocr·intellij-idea·idea
绛橘色的日落(。・∀・)ノ14 小时前
C#学习笔记
笔记·学习·c#
ss27314 小时前
AI时代首先数据库-PostgreSQL实战笔记 | 16 PostgreSQL子查询与嵌套查询(下)
数据库·笔记·postgresql
云水初14 小时前
【项目篇】基于LanGraph的自动处理客服邮件的 Agent汇总
开发语言·笔记·python·学习·fastapi·langgraph