当前位置: 首页 > news >正文

每日算法刷题Day66:9.8:leetcode 网格图dfs14道题,用时2h30min

二.网格图DFS

1.套路

1.适用于需要计算连通块个数、大小的题目。
网格图递归:
递归入口:网格图的某个格子
递归方向:一般为左右上下的相邻格子
递归边界:出界、遇到障碍或者已访问
2.本质还是连通块思路,只不过要再判断“出界、遇到障碍”
代码模版

class Solution {
public:vector<vector<bool>> vis;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};int n, m;bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<char>>& grid) {vis[x][y] = true; // 访问(x,y)// check(x,y) // 检测(x,y)是否符合特殊条件for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid[nx][ny] == '1' && !vis[nx][ny])dfs(nx, ny, grid);}}int numIslands(vector<vector<char>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {// 只进行基础判断if (grid[i][j] == '1' && !vis[i][j]) {dfs(i, j, grid);++res;}}}return res;}
};
2.题目描述
3.学习经验

1.题目特殊条件可以让枚举四个方向变为八个或者固定的(记录朝向)

1. 200. 岛屿数量(中等,无回溯,有vis)

200. 岛屿数量 - 力扣(LeetCode)

思想

1.给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
2.本质是计算连通块的数量

代码
class Solution {
public:vector<vector<bool>> vis;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};int n, m;bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<char>>& grid) {vis[x][y] = true;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid[nx][ny] == '1' && !vis[nx][ny])dfs(nx, ny, grid);}}int numIslands(vector<vector<char>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] == '1' && !vis[i][j]) {dfs(i, j, grid);++res;}}}return res;}
};
2. 695. 岛屿的最大面积(中等,无回溯,有vis)

695. 岛屿的最大面积 - 力扣(LeetCode)

思想

1.给你一个大小为 m x n 的二进制矩阵 grid 。
岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
岛屿的面积是岛上值为 1 的单元格的数目。
计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
2.本质是求最大连通块的大小

代码
class Solution {
public:int n, m;vector<vector<bool>> vis;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }int dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = true;int size = 1;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid[nx][ny] == 1 && !vis[nx][ny])size += dfs(nx, ny, grid);}return size;}int maxAreaOfIsland(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] == 1 && !vis[i][j]) {int size = dfs(i, j, grid);res = max(res, size);}}}return res;}
};
3. 3619. 总价值可以被K整除的岛屿数量(中等,无回溯,有vis)

3619. 总价值可以被 K 整除的岛屿数目 - 力扣(LeetCode)

思想

1.给你一个 m x n 的矩阵 grid 和一个正整数 k。一个 岛屿 是由  整数(表示陆地)组成的,并且陆地间 四周 连通(水平或垂直)。
一个岛屿的总价值是该岛屿中所有单元格的值之和。
返回总价值可以被 k 整除 的岛屿数量。
2.本质还是遍历连通块

代码
class Solution {
public:int n, m;vector<vector<bool>> vis;typedef long long ll;ll cnt;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {cnt += grid[x][y];vis[x][y] = true;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid[nx][ny] > 0 && !vis[nx][ny])dfs(nx, ny, grid);}}int countIslands(vector<vector<int>>& grid, int k) {n = grid.size(), m = grid[0].size();int res = 0;vis.assign(n, vector<bool>(m, false));for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] > 0 && !vis[i][j]) {cnt = 0;dfs(i, j, grid);if (cnt % k == 0)++res;}}}return res;}
};
4. 面试题 16.19. 水域大小(中等,无回溯,有vis)

面试题 16.19. 水域大小 - 力扣(LeetCode)

思想

1.你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。
2."对角连接"增加遍历范围,从4个变成8个即可

代码
class Solution {
public:int n, m;vector<vector<bool>> vis;vector<int> dx = {1, -1, 0, 0, 1, 1, -1, -1},dy = {0, 0, 1, -1, 1, -1, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }int dfs(int x, int y, vector<vector<int>>& land) {vis[x][y] = true;int size = 1;for (int i = 0; i < 8; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && land[nx][ny] == 0 && !vis[nx][ny])size += dfs(nx, ny, land);}return size;}vector<int> pondSizes(vector<vector<int>>& land) {n = land.size(), m = land[0].size();vis.assign(n, vector<bool>(m, false));vector<int> res;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (land[i][j] == 0 && !vis[i][j]) {res.push_back(dfs(i, j, land));}}}sort(res.begin(), res.end());return res;}
};
5. LCS 03. 主题空间(中等,无回溯,有vis)
思想

1.「以扣会友」线下活动所在场地由若干主题空间与走廊组成,场地的地图记作由一维字符串型数组 grid,字符串中仅包含 "0"~"5" 这 6 个字符。地图上每一个字符代表面积为 1 的区域,其中 "0" 表示走廊,其他字符表示主题空间。相同且连续(连续指上、下、左、右四个方向连接)的字符组成同一个主题空间。
假如整个 grid 区域的外侧均为走廊。请问,不与走廊直接相邻的主题空间的最大面积是多少?如果不存在这样的空间请返回 0
2.本质上是遍历每个连通块,在遍历的时候判断”是否与走廊直接相邻“,然后标记完一个主题空间后根据判断结果更新答案

代码
class Solution {
public:vector<vector<bool>> vis;int n, m;bool tag;int cnt;char start;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<string>& grid) {vis[x][y] = true;++cnt;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (!inMap(nx, ny) || grid[nx][ny] == '0') {tag = false;continue;}if (!vis[nx][ny] && grid[nx][ny] == start)dfs(nx, ny, grid);}}int largestArea(vector<string>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] > '0' && !vis[i][j]) {tag = true;cnt = 0;start = grid[i][j];dfs(i, j, grid);if (tag)res = max(res, cnt);}}}return res;}
};
6. 463. 岛屿的周长(简单,无回溯,有vis)

463. 岛屿的周长 - 力扣(LeetCode)

思想

1.给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
2.本质是求一个连通块的数量,以及边的数量(两个邻居相邻的边可以理解为图两个节点的边)

代码
class Solution {
public:int n, m;vector<vector<bool>> vis;int size;int dif;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = true;++size;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid[nx][ny] == 1 ){++dif; // 当前节点(x,y)减去一条边if(!vis[nx][ny])    dfs(nx, ny, grid);}}}int islandPerimeter(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();int res = 0;vis.assign(n, vector<bool>(m, false));size=0,dif=0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] == 1 && !vis[i][j]) {dfs(i, j, grid);res = 4 * size - dif;}}}return res;}
};
7. 2658. 网格图中鱼的最大数目(中等,无回溯,有vis)

2658. 网格图中鱼的最大数目 - 力扣(LeetCode)

思想

1.给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid ,其中下标在 (r, c) 处的整数表示:

  • 如果 grid[r][c] = 0 ,那么它是一块 陆地 。
  • 如果 grid[r][c] > 0 ,那么它是一块 水域 ,且包含 grid[r][c] 条鱼。
    一位渔夫可以从任意 水域 格子 (r, c) 出发,然后执行以下操作任意次:
  • 捕捞格子 (r, c) 处所有的鱼,或者
  • 移动到相邻的 水域 格子。
    请你返回渔夫最优策略下, 最多 可以捕捞多少条鱼。如果没有水域格子,请你返回 0 。
    格子 (r, c) 相邻 的格子为 (r, c + 1) ,(r, c - 1) ,(r + 1, c) 和 (r - 1, c) ,前提是相邻格子在网格图内。
    2.本质是求连通块的数字和的最大值
代码
class Solution {
public:vector<vector<bool>> vis;int n, m;int size;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = true;size += grid[x][y];for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid[nx][ny] > 0 && !vis[nx][ny])dfs(nx, ny, grid);}}int findMaxFish(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] > 0 && !vis[i][j]) {size = 0;dfs(i, j, grid);res = max(res, size);}}}return res;}
};
8. 1034. 边界着色(中等,无回溯,有vis)

1034. 边界着色 - 力扣(LeetCode)

思想

1.给你一个大小为 m x n 的整数矩阵 grid ,表示一个网格。另给你三个整数 rowcol 和 color 。网格中的每个值表示该位置处的网格块的颜色。
如果两个方块在任意 4 个方向上相邻,则称它们 相邻 。
如果两个方块具有相同的颜色且相邻,它们则属于同一个 连通分量 。
连通分量的边界 是指连通分量中满足下述条件之一的所有网格块:

  • 在上、下、左、右任意一个方向上与不属于同一连通分量的网格块相邻
  • 在网格的边界上(第一行/列或最后一行/列)
    请你使用指定颜色 color 为所有包含网格块 grid[row][col] 的 连通分量的边界 进行着色。
    并返回最终的网格 grid 。
    2.因为题目要求修改原grid的颜色,而一旦边搜索边修改会影响其他点的判断,所以依旧采取dfs标记,标记完后统一修改。这里的优化是标记完确定要着色,将待着色的点放入数组中,dfs完后直接遍历着色数组即可
代码
class Solution {
public:int n, m;vector<vector<int>> vis;int start;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = 1;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (!inMap(nx, ny) || grid[nx][ny] != start) {vis[x][y] = 2;continue;}if (!vis[nx][ny])dfs(nx, ny, grid);}}vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col,int color) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<int>(m, 0));start = grid[row][col];dfs(row, col, grid);for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (vis[i][j] == 2)grid[i][j] = color;}}return grid;}
};

优化,在dfs里面存储着色点对数组,dfs遍历完后遍历数组,而不再两层遍历

class Solution {
public:int n, m;vector<vector<int>> vis;int start;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};typedef pair<int, int> PII;vector<PII> change;bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = 1;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (!inMap(nx, ny) || grid[nx][ny] != start) {vis[x][y] = 2;continue;}if (!vis[nx][ny])dfs(nx, ny, grid);}if (vis[x][y] == 2)change.push_back({x, y});}vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col,int color) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<int>(m, 0));start = grid[row][col];dfs(row, col, grid);for (auto& tmp : change) {grid[tmp.first][tmp.second] = color;}return grid;}
};
9. 1020. 飞地的数量(中等,无回溯,有vis)

1020. 飞地的数量 - 力扣(LeetCode)

思想

1.给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。
一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。
返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。
2.本质是在遍历标记连通块的过程中判断这个连通块靠不靠近边界

代码
class Solution {
public:vector<vector<bool>> vis;int n, m;bool tag;int size;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = true;++size;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (!inMap(nx, ny)) {tag = false;continue;}if (grid[nx][ny] == 1 && !vis[nx][ny])dfs(nx, ny, grid);}}int numEnclaves(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] == 1 && !vis[i][j]) {tag = true;size = 0;dfs(i, j, grid);if (tag)res += size;}}}return res;}
};
10. 2684. 矩阵中移动的最大次数(中等,无回溯,无vis)

2684. 矩阵中移动的最大次数 - 力扣(LeetCode)

思想

1.给你一个下标从 0 开始、大小为 m x n 的矩阵 grid ,矩阵由若干  整数组成。
你可以从矩阵第一列中的 任一 单元格出发,按以下方式遍历 grid :

  • 从单元格 (row, col) 可以移动到 (row - 1, col + 1)(row, col + 1) 和 (row + 1, col + 1) 三个单元格中任一满足值 严格 大于当前单元格的单元格。
    返回你在矩阵中能够 移动 的 最大 次数。
    2.题目是要找所有路径的最大次数,是递归返回路径的次数,无需知道路径细节,所以无需回溯。因为移动只向右侧移动,所以不会死循环,无需vis数组。
    但是一个点会覆盖多条路径,会算多次,所以可以拿个数组记录从它开始的最大移动次数,只需dfs计算一次,进行记忆化搜索。
代码
class Solution {
public:vector<vector<int>> cnts;int n, m;int dfs(int x, int y, vector<vector<int>>& grid) {if (cnts[x][y] > 0)return cnts[x][y];if (y == m - 1)return 0;int cnt = 0;if (x - 1 >= 0 && grid[x - 1][y + 1] > grid[x][y])cnt = max(cnt, dfs(x - 1, y + 1, grid) + 1);if (grid[x][y + 1] > grid[x][y])cnt = max(cnt, dfs(x, y + 1, grid) + 1);if (x + 1 < n && grid[x + 1][y + 1] > grid[x][y])cnt = max(cnt, dfs(x + 1, y + 1, grid) + 1);cnts[x][y] = cnt;return cnt;}int maxMoves(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();cnts.assign(n, vector<int>(m, 0));int res = 0;for (int i = 0; i < n; ++i) {res = max(res, dfs(i, 0, grid));}return res;}
};
11. 1254. 统计封闭岛屿的数目(中等)

1254. 统计封闭岛屿的数目 - 力扣(LeetCode)

思想

1.二维矩阵 grid 由 0 (土地)和 1 (水)组成。岛是由最大的4个方向连通的 0 组成的群,封闭岛是一个 完全 由1包围(左、上、右、下)的岛。
请返回 封闭岛屿 的数目。
2.本质是判断连通块是否与网格图边界相连

代码
class Solution {
public:vector<vector<bool>> vis;int n, m;bool tag;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid) {vis[x][y] = true;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (!inMap(nx, ny)) {tag = false;continue;}if (grid[nx][ny] == 0 && !vis[nx][ny])dfs(nx, ny, grid);}}int closedIsland(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid[i][j] == 0 && !vis[i][j]) {tag = true;dfs(i, j, grid);if (tag)++res;}}}return res;}
};
12. 130. 被围绕的区域(中等,学习,无回溯,有vis)

130. 被围绕的区域 - 力扣(LeetCode)

思想

1.给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' 组成,捕获 所有 被围绕的区域

  • **连接:**一个单元格与水平或垂直方向上相邻的单元格连接。
  • 区域:连接所有 'O' 的单元格来形成一个区域。
  • 围绕:如果您可以用 'X' 单元格 连接这个区域,并且区域中没有任何单元格位于 board 边缘,则该区域被 'X' 单元格围绕。
    通过 原地 将输入矩阵中的所有 'O' 替换为 'X' 来 捕获被围绕的区域。你不需要返回任何值。
    2.这题本质也是判断一个连通块是否接触边界,然后所有连通块遍历完了将不接触边界的一整块替换,所以这里面不仅要判断这个节点是否被遍历,还要判断它属于哪块连通块,所以vis数组要开int的,只有-1表示未遍历,利用一个自增id来记录是几号连通块,再利用一个判断数组判断这块连通块是否要替换。
    3.优化方法是
    从边界上的O出发
    ,标记访问过的O,所以最终访问过的O不用替换,未访问的不用替换,将O分为属于边界连通块的和不属于边界连通块的,无需判断它属于具体哪一块,更简单
代码

正常遍历:

class Solution {
public:vector<vector<int>> vis;vector<bool> change;int n, m;bool tag;int id;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<char>>& board) {vis[x][y] = id;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (!inMap(nx, ny)) {tag = false;continue;}if (board[nx][ny] == 'O' && vis[nx][ny] == -1)dfs(nx, ny, board);}}void solve(vector<vector<char>>& board) {n = board.size(), m = board[0].size();vis.assign(n, vector<int>(m, -1));id = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (board[i][j] == 'O' && vis[i][j] == -1) {tag = true;dfs(i, j, board);change.push_back(tag);++id;}}}for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (board[i][j] == 'O' && change[vis[i][j]]) {board[i][j] = 'X';}}}}
};

边界出发优化:

class Solution {
public:vector<vector<bool>> vis;int n, m;bool tag;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<char>>& board) {vis[x][y] = true;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && board[nx][ny] == 'O' && !vis[nx][ny])dfs(nx, ny, board);}}void solve(vector<vector<char>>& board) {n = board.size(), m = board[0].size();vis.assign(n, vector<bool>(m, false));// 边界访问for(int i=0;i<n;++i){if(board[i][0]=='O' && !vis[i][0])  dfs(i,0,board);if(board[i][m-1]=='O' && !vis[i][m-1])  dfs(i,m-1,board);}for(int j=1;j<m-1;++j){if(board[0][j]=='O' && !vis[0][j])  dfs(0,j,board);if(board[n-1][j]=='O' && !vis[n-1][j])  dfs(n-1,j,board);}for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (board[i][j] == 'O' && !vis[i][j]) {board[i][j] = 'X';}}}}
};
13. 1905. 统计子岛屿(中等,无回溯,有vis)

1905. 统计子岛屿 - 力扣(LeetCode)

思想

1.给你两个 m x n 的二进制矩阵 grid1 和 grid2 ,它们只包含 0 (表示水域)和 1 (表示陆地)。一个 岛屿 是由 四个方向 (水平或者竖直)上相邻的 1 组成的区域。任何矩阵以外的区域都视为水域。
如果 grid2 的一个岛屿,被 grid1 的一个岛屿 完全 包含,也就是说 grid2 中该岛屿的每一个格子都被 grid1 中同一个岛屿完全包含,那么我们称 grid2 中的这个岛屿为 子岛屿 。
请你返回 grid2 中 子岛屿 的 数目 。
2.我的想法是先dfs grid1标记岛屿,然后dfs grid2标记岛屿,且不是grid1的岛屿则全局变量变成false,但这样麻烦了,不是grid1的岛屿即水域,在grid1中天然就是0,即grid2中当前岛屿一个位置在grid1中为0,则它必定不是子岛屿,反之则是,一次dfs grid2即可

代码
class Solution {
public:int n, m;vector<vector<bool>> vis1, vis2;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool tag;bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs1(int x, int y, vector<vector<int>>& grid1) {vis1[x][y] = true;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid1[nx][ny] == 1 && !vis1[nx][ny])dfs1(nx, ny, grid1);}}void dfs2(int x, int y, vector<vector<int>>& grid2) {vis2[x][y] = true;if (!vis1[x][y])tag = false;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid2[nx][ny] == 1 && !vis2[nx][ny])dfs2(nx, ny, grid2);}}int countSubIslands(vector<vector<int>>& grid1,vector<vector<int>>& grid2) {n = grid1.size(), m = grid1[0].size();vis1.assign(n, vector<bool>(m, false));vis2.assign(n, vector<bool>(m, false));for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid1[i][j] == 1 && !vis1[i][j])dfs1(i, j, grid1);}}int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid2[i][j] == 1 && !vis2[i][j]) {tag = true;dfs2(i, j, grid2);if (tag)++res;}}}return res;}
};

优化一次dfs:

class Solution {
public:int n, m;vector<vector<bool>> vis;vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};bool tag;bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }void dfs(int x, int y, vector<vector<int>>& grid2,vector<vector<int>>& grid1) {vis[x][y] = true;if (grid1[x][y] == 0)tag = false;for (int i = 0; i < 4; ++i) {int nx = x + dx[i], ny = y + dy[i];if (inMap(nx, ny) && grid2[nx][ny] == 1 && !vis[nx][ny])dfs(nx, ny, grid2, grid1);}}int countSubIslands(vector<vector<int>>& grid1,vector<vector<int>>& grid2) {n = grid1.size(), m = grid1[0].size();vis.assign(n, vector<bool>(m, false));int res = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (grid2[i][j] == 1 && !vis[i][j]) {tag = true;dfs(i, j, grid2, grid1);if (tag)++res;}}}return res;}
};
14. 1391. 检查网格中是否存在有效路径(中等,学习模拟,无回溯,有vis)

1391. 检查网格中是否存在有效路径 - 力扣(LeetCode)

思想

1.给你一个 m x n 的网格 grid。网格里的每个单元都代表一条街道。grid[i][j] 的街道可以是:

  • 1 表示连接左单元格和右单元格的街道。
  • 2 表示连接上单元格和下单元格的街道。
  • 3 表示连接左单元格和下单元格的街道。
  • 4 表示连接右单元格和下单元格的街道。
  • 5 表示连接左单元格和上单元格的街道。
  • 6 表示连接右单元格和上单元格的街道。
    2.跟传统的网格图不同,传统的网格图在当前位置搜索下一个位置四个方向都可以,而此题从某个朝向进入当前位置,要么不能走这个街道,要么会有一个新朝向,这个新朝向保证了下一个位置唯一,所以逻辑为:
  • (1)枚举四个初始朝向(因为初始街道有的两个朝向都行),开始dfs
  • (2)进入当前街道,标记访问,进行判断
    • (2.1)当前朝向不能走这条街道,返回false
    • (2.2)当前朝向能走这条街道:
      • (2.2.1)为最终位置,返回true
      • (2.2.2)不是最终位置,得到走完当前街道的新朝向,得到新位置,判断在地图且未访问过,则继续dfs
        因为是判断是否存在有效路径,所以dfs返回bool且在探索下一个位置时作为if中的一个条件使用,跟[[十.图论算法-基础遍历#2. 1971. 寻找图中是否存在路径(简单,重点学习,查询一次路径是否存在,无回溯,有vis)]]一样
代码
class Solution {
public:// 朝向:0:下,1:上,2:右,3:左vector<int> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};// 街道编号,朝向->新朝向,-1不行vector<vector<int>> vec = {{-1, -1, -1, -1}, {-1, -1, 2, 3}, {0, 1, -1, -1}, {-1, 3, 0, 01},{-1, 2, -1, 0},   {3, -1, 1, -1}, {2, -1, -1, 1},};int n, m;vector<vector<bool>> vis;bool inMap(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; }bool dfs(int x, int y, vector<vector<int>>& grid, int chao) {vis[x][y] = true;int idx = grid[x][y];int newChao = vec[idx][chao];// 先判断当前访问位置if (newChao == -1)return false;if (x == n - 1 && y == m - 1)return true;int nx = x + dx[newChao], ny = y + dy[newChao];if (inMap(nx, ny) && !vis[nx][ny] && dfs(nx, ny, grid, newChao))return true;return false;}bool hasValidPath(vector<vector<int>>& grid) {n = grid.size(), m = grid[0].size();vis.assign(n, vector<bool>(m, false));bool tag = false;for (int i = 0; i < 4; ++i) {if (dfs(0, 0, grid, i)) {tag = true;break;}}return tag;}
};

文章转载自:

http://er70ntT0.bzLgb.cn
http://eXKuGOKf.bzLgb.cn
http://Mlb09bq6.bzLgb.cn
http://HUHZ60aQ.bzLgb.cn
http://gqNeUGwC.bzLgb.cn
http://UBdt9j2V.bzLgb.cn
http://I7rqbHPA.bzLgb.cn
http://VnWHPCQ6.bzLgb.cn
http://btCCQa0N.bzLgb.cn
http://4uL3np3F.bzLgb.cn
http://kAQOLOT4.bzLgb.cn
http://eVOCFlEv.bzLgb.cn
http://O2T05Ivq.bzLgb.cn
http://oQEd2LN8.bzLgb.cn
http://voh8COrW.bzLgb.cn
http://tA0QM4yD.bzLgb.cn
http://3ksqjORa.bzLgb.cn
http://Ere26GT0.bzLgb.cn
http://u7542tIf.bzLgb.cn
http://2kWAC4rw.bzLgb.cn
http://FykncibU.bzLgb.cn
http://BByt4mBz.bzLgb.cn
http://UaYN3zyT.bzLgb.cn
http://DeD4vWXd.bzLgb.cn
http://ZAL8blcM.bzLgb.cn
http://uN7X9pX7.bzLgb.cn
http://i4ouAa8c.bzLgb.cn
http://rTkrcEht.bzLgb.cn
http://F3UQTTDT.bzLgb.cn
http://mjaH4rOe.bzLgb.cn
http://www.dtcms.com/a/375089.html

相关文章:

  • html css js网页制作成品——HTML+CSS无穷网页设计(5页)附源码
  • 服务器数据恢复—Raid6阵列崩溃导致上层分区无法访问的数据恢复案例
  • 机器学习实操项目01——Numpy入门(基本操作、数组形状操作、复制与试图、多种索引技巧、线性代数)
  • WPS智能写作
  • 预编译SQL:安全与性能的双重保障
  • Gin + Zap 日志:构建高性能、结构化的应用日志系统
  • PortSwigger靶场之Reflected XSS into attribute with angle brackets HTML-encoded通关秘籍
  • EasyExcel:快速读写Excel的工具类
  • 基于Room+RESTful的双权限Android开机时间监控方案
  • 串口数据收发的设计
  • 基于Nginx实现反向代理、负载均衡与动静分离完整部署指南
  • Excel 表格 - Excel 单元格添加边框
  • 产品无法正确解析复杂表格和流程图,有什么替代方案或优化方法?
  • C++ -- 模板
  • C# ObjectListView实现树状文件夹浏览
  • 高级 RAG 技术原理和前沿进展
  • 42.Shell脚本判断和if语句及相关案例
  • Game Runtime Libraries Package 解决游戏运行的痛点困境
  • 《P3825 [NOI2017] 游戏》
  • 第三课、Cocos Creator 项目创建与目录结构详解
  • C#中的浅拷贝与深拷贝
  • docker 整理几个常用的指令
  • Git上有更新而本地无更新时的解决方案
  • Doc2X为一切AI文档服务的基础设施,将PDF转换为Word、HTML、LaTeX、Markdown等
  • k8s 内置的containerd配置阿里云个人镜像地址及认证
  • 新节点加入k8s集群命令查看
  • 在 PostgreSQL中查看有哪些用户
  • 【从零开始的大模型原理与实践教程】--第一章:NLP基础概念
  • 零侵入式对接美团核销接口的技术合作模式
  • Kafka面试精讲 Day 14:集群扩容与数据迁移