2025.10.9 17:26 开新坑 😃

# 网格图DFS

适用于需要计算连通块个数、大小以及对连通块的操作的题目,反正其关键词在于 连通块

网格图的遍历顺序图怎么画???

# 模板

完整模板
int m=grid.size(),n=grid[0].size();
        int dirs[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
        function<void(int,int)> dfs=[&](int x,int y){
            if(x<0||y<0||x>=m||y>=n||) return ;//越界或者该点已经访问过了,返回
            grid[x][y]= ;//将该点标记为访问过了
            for(auto [dx,dy]:dirs){
                int nx=x+dx,ny=y+dy;
                dfs(nx,ny);
            }
        };
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                dfs(i,j);
            }
        }
    

        function<void(int,int)> dfs=[&](int x,int y){
            if(grid[x][y]==0) return ;//如果该点已经访问,则
            ans=max(ans,y);
            for(auto [dx,dy]:dirs){
                int nx=x+dx,ny=y+dy;
                if(nx>=0&&nx<m&&ny>=0&&ny<n&&grid[nx][ny]>grid[x][y]) dfs(nx,ny);
            }
            grid[x][y]=0;//将该点标记为访问过了
        };

需要考虑 什么时候继续dfs? 如何判断重复访问? 对越界的判断.有何作用

200. 岛屿数量

哥伦布插旗子,每当遇到一个岛的时候,就用dfs将其完全标记插满旗子
此时由于每个节点最多被 访问一次,(被标记的地方不会进入函数)\
int DIRS[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; // 左右上下
dfs_target=[&](int i,int j){//利用dfs对该岛屿进行标记
            if(i<0||i>=n||j<0||j>=m||grid[i][j]!='1'){
                return 0;//越界或当前是水
            }
    		int area=1;
    		grid[i][j]=1;
            for (auto [dx, dy] : DIRS) {
                area += dfs_target(x+dx, y+dy);
            }
    		return area;
};
for(){
    for(){
        dfs_target;//遇到新岛,将其标记
        ans++;
    }
}

3619. 总价值可以被 K 整除的岛屿数目

dfs=[&](int i,int j){
            if(i<0||i>=m||j<0||j>=n||grid[i][j]==0) return ;
            cur=(cur+=grid[i][j])%k;
    		grid[i][j]=0;
    		for(auto [dx,dy] : DIRS){
                dfs(i+dx,j+dy);
            }
        };

# 边界问题

1034. 边界着色 1020. 飞地的数量

class Solution {
public:
    vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col,int color) {
        // 如果邻居越界或颜色不同,当前点是边界
        int m = grid.size(), n = grid[0].size(), start = grid[row][col];
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        int dirs[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
        vector<pair<int, int>> borders;
        if (color == start)
            return grid;
        function<void(int, int)> dfs = [&](int x, int y) {
            if (visited[x][y])   return;
            visited[x][y] = true;
            bool isBorder = false;
            for (auto [dx, dy] : dirs) {
                int nx = x + dx, ny = y + dy;
                if (nx < 0 || ny < 0 || nx >= m || ny >= n) {
                    isBorder = true;
                } else if (grid[nx][ny] != start) {
                    isBorder = true;
                } else {
                    dfs(nx, ny);
                }
            }
            if (isBorder) {
                borders.emplace_back(x, y);
            }
        };
        dfs(row, col);
        for (auto [x, y] : borders) {
            grid[x][y] = color;
        }
        return grid;
    }
};

# 逆向思维

130. 被围绕的区域1020. 飞地的数量

逆向思维,要找到被围绕的区域,不妨先找到含边界的区域.
在边界上找到的区域,其一定有边界,hhhhh
for(int i=0;i<m;i++){
       if(grid[i][0]==1) dfs(i,0); 
       if(grid[i][n-1]==1) dfs(i,n-1);
}
 for(int j=0;j<n;j++){
       if(grid[0][j]==1) dfs(0,j);
       if(grid[m-1][j]==1) dfs(m-1,j);
}
    
对于130,可以先将所有边界上的区域标记为 # ,此后 仅被包围区域有0,然后再次遍历,将0改为x,#恢复为0
    
对于1020,先将边界区域改为0,而后再次遍历统计剩余1的数量即可

2684. 矩阵中移动的最大次数

dfs/dp 都可以
1. 动态规划
    下标含义: dp[i][j]从ij位置的最大移动次数
    初始化:最后一列一定为0,因为无法再移动
    递推公式: dp[i][j]=max(dp[i-1][j-1],dp[i][j-1],dp[i+1][j-1])+1;
	遍历顺序 因为只要知道后面一列的所有值,就能判断当前列的值,所以是从右往左,每次遍历一列
2. dfs 但优化
    //对于已经访问过的点,其已经进行了dfs更新了最佳答案,没必要再次访问
    function<void(int, int)> dfs = [&](int i, int j) {
            ans = max(ans, j);
            if (ans == n - 1) return; // ans 已达到最大
            // 向右上/右/右下走一步
            for (int k = max(i - 1, 0); k < min(i + 2, m); k++) {
                if (grid[k][j + 1] > grid[i][j]) {
                    dfs(k, j + 1);
                }
            }
            grid[i][j] = 0;
        };

# 判断是否访问的问题

一般情况下,对于访问过的点,修改其数值就可以标记该点是否已经访问,如,访问过陆地后将其标记为水域, 但对于有些题目 ,有可能

原数组并不能随便修改,或者标记后跟没标记一样, 此时 就可以

eg.1765. 地图中的最高点 先将陆地都标记为-1,表示没访问过

1034. 边界着色 先将要修改的边界存起来,最后再修改

# 网格图BFS

DFS 是不撞南墙不回头;BFS 是先访问近的,再访问远的,故BFS一般用来解决最近路径问题

# 模板

模板
int m=grid.size(),n=grid[0].size(),ans=1;
        queue<pair<int,int>> que;
        que.push({0,0});
        grid[0][0]=1;
        while(!que.empty()){
            int size=que.size();//保证只每次访问一层
            for(int i=0;i<size;i++){
                auto [x,y]=que.front();
                que.pop();
                if(x==n-1&&y==n-1) return ans;//找到出口了,返回答案
                for(auto [dx,dy]:dirs){
                    int nx=x+dx,ny=y+dy;
                    if (nx >= 0 && nx < m && ny >= 0 && ny < n &&grid[nx][ny]==0){
                        //当前点未出界且确实要访问
                        grid[nx][ny]=1;//标记为已经访问,防止调头
                        que.push({nx,ny});
                    }
                }
            }
            ans++;
        }

1926. 迷宫中离入口最近的出口

 int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {
        int m = maze.size(), n = maze[0].size();
        int dirs[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; // 修正方向数组
        queue<pair<int, int>> que;
        que.push({entrance[0], entrance[1]});
        maze[entrance[0]][entrance[1]] = '+'; // 入口标记为已访问
        int steps = 0;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                auto [x, y] = que.front();
                que.pop();
                // 检查是否到达出口(边界且不是入口)
                if ((x == 0 || x == m - 1 || y == 0 || y == n - 1) &&
                    !(x == entrance[0] && y == entrance[1])) {
                    return steps;//到达边界且不为入口时说明找到了答案
                }
                for (auto [dx, dy] : dirs) {
                    int nx = x + dx, ny = y + dy;
                    if (nx >= 0 && nx < m && ny >= 0 && ny < n &&
                        maze[nx][ny] == '.') {
                        maze[nx][ny] = '+'; // 将其标记为墙,否则会重复
                        que.push({nx, ny});
                    }
                }
            }
            steps++;
        }
        return -1;
    }

# 多源BFS

1162. 地图分析

从各个陆地出发bfs,曼哈顿距离恰好为当前bfs访问的层数
而且当前海洋点被访问时,dis恰好为其到陆地的最近距离(因为从陆地出发大家都是每次走一步,谁先到该海洋点谁就里它最近)

image.png

994. 腐烂的橘子

2025.10.19 下午16:20 网格图入门结束