代码随想录-算法训练营-番外(图论03:孤岛的总面积,沉没孤岛,水流问题,建造最大岛屿)
day03 图论part03
今日任务:孤岛的总面积,沉没孤岛,水流问题,建造最大岛屿
代码总量有点多但是不难理解
https://www.programmercarl.com/kamacoder/0101.孤岛的总面积.html#思路
往日任务:
day01 图论part01
今日任务:图论理论基础/所有可到达的路径
代码随想录图论视频部分还没更新
https://programmercarl.com/kamacoder/图论理论基础.html#图的基本概念
day02 图论part02
今日任务:岛屿数量,岛屿的最大面积
都是一个模子套出来的
https://programmercarl.com/kamacoder/0099.岛屿的数量深搜.html#思路
day03
孤岛的总面积
bfs如下,dfs不需要改变主函数
//特殊之处在于直接在原地图grid上进行赋值,实现visited功能 //以及在主函数中对贴边岛屿进行了消除处理 import java.util.*; public class Main { private static int count = 0;//陆地面积 private static final int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 四个方向 private static void bfs(int[][] grid, int x, int y) {//广搜 Queue<int[]> que = new LinkedList<>(); que.add(new int[]{x, y}); grid[x][y] = 0; // 只要加入队列,立刻标记 count++; while (!que.isEmpty()) {//队列不为空,就取出来遍历四个方向判断合法性 //如果不超边界而且是陆地,加进队列,设为0代表遍历过,count++ int[] cur = que.poll(); int curx = cur[0]; int cury = cur[1]; for (int i = 0; i < 4; i++) { int nextx = curx + dir[i][0]; int nexty = cury + dir[i][1]; if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue; // 越界了,直接跳过 if (grid[nextx][nexty] == 1) { que.add(new int[]{nextx, nexty}); count++; grid[nextx][nexty] = 0; // 只要加入队列立刻标记 } } } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int m = scanner.nextInt(); int[][] grid = new int[n][m]; // 读取网格 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { grid[i][j] = scanner.nextInt(); } } // 从左侧边,和右侧边向中间遍历 //左右两边贴边的岛屿都设为0了 for (int i = 0; i < n; i++) { if (grid[i][0] == 1) bfs(grid, i, 0); if (grid[i][m - 1] == 1) bfs(grid, i, m - 1); } // 从上边和下边向中间遍历 //上下边界贴边的岛屿设为0 for (int j = 0; j < m; j++) { if (grid[0][j] == 1) bfs(grid, 0, j); if (grid[n - 1][j] == 1) bfs(grid, n - 1, j); } count = 0;//计算孤岛面积 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (grid[i][j] == 1) bfs(grid, i, j); //遍历剩下的岛屿,也就是孤岛 } } System.out.println(count); } }
沉没孤岛
深搜,广搜只需要改变dfs即可
//主函数:贴边岛屿设为2,然后遍历把1变成0 并且 把2变成1 import java.util.Scanner; public class Main { static int[][] dir = { {-1, 0}, {0, -1}, {1, 0}, {0, 1} }; // 保存四个方向 public static void dfs(int[][] grid, int x, int y) { grid[x][y] = 2; for (int[] d : dir) { int nextX = x + d[0]; int nextY = y + d[1]; // 超过边界 if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) continue; // 不符合条件,不继续遍历 if (grid[nextX][nextY] == 0 || grid[nextX][nextY] == 2) continue; dfs(grid, nextX, nextY); } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int m = scanner.nextInt(); int[][] grid = new int[n][m]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { grid[i][j] = scanner.nextInt(); } } // 步骤一: // 从左侧边,和右侧边 向中间遍历 for (int i = 0; i < n; i++) { if (grid[i][0] == 1) dfs(grid, i, 0); if (grid[i][m - 1] == 1) dfs(grid, i, m - 1); } // 从上边和下边 向中间遍历 for (int j = 0; j < m; j++) { if (grid[0][j] == 1) dfs(grid, 0, j); if (grid[n - 1][j] == 1) dfs(grid, n - 1, j); } // 步骤二、步骤三 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (grid[i][j] == 1) grid[i][j] = 0; if (grid[i][j] == 2) grid[i][j] = 1; } } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { System.out.print(grid[i][j] + " "); } System.out.println(); } scanner.close(); } }
水流问题
//两个visited,如果firstborder[i][j]==1 && secondborder[i][j]==1则可以到达两个边界 import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int m = sc.nextInt(); int n = sc.nextInt(); int[][] grid = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { grid[i][j] = sc.nextInt(); } } int[][] firstborder = new int[m][n]; int[][] secondborder = new int[m][n]; for (int i = 0; i < m; i++) { dfs(grid, i,0, firstborder ); dfs(grid, i,n - 1, secondborder ); } for (int j = 0; j < n; j++) { dfs(grid, 0, j, firstborder ); dfs(grid, m-1, j, secondborder ); } // // List<List<Integer>> res = new ArrayList<>(); // for (int i = 0; i < m; i++) { // for (int j = 0; j < n; j++) { // if(firstborder[i][j]!=0 && secondborder[i][j]!=0) { // // res.add(Arrays.asList(i,j)); // System.out.println(i + " " + j); // } // } // 当两个边界二维数组在某个位置都为true时,符合题目要求 List<List<Integer>> res = new ArrayList<>(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (firstborder[i][j]==1 && secondborder[i][j]==1) { res.add(Arrays.asList(i, j)); } } } // 打印结果 for (List<Integer> list : res) { for (int k = 0; k < list.size(); k++) { if (k == 0) { System.out.print(list.get(k) + " "); } else { System.out.print(list.get(k)); } } System.out.println(); } } static int[][] dir = { {-1, 0}, {0, -1}, {1, 0}, {0, 1} }; public static void dfs(int[][] grid, int x, int y, int[][] visited){ if (visited[x][y] != 0) return; visited[x][y] = 1; for (int[] d : dir) { int nextX = x + d[0]; int nextY = y + d[1]; // 超过边界 if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) continue; // 不符合条件,不继续遍历 if (visited[nextX][nextY] == 1 || grid[nextX][nextY] < grid[x][y]) continue;//visited和grid dfs(grid, nextX, nextY, visited); } } }
建造最大岛屿
//判断是不是全是陆地 //判断水格子周围有没有其他编号的陆地块 import java.util.HashMap; import java.util.HashSet; import java.util.Scanner; import java.util.Set; public class Main{ // 该方法采用 DFS // 定义全局变量 // 记录每次每个岛屿的面积 static int count; // 对每个岛屿进行标记 static int mark; // 定义二维数组表示四个方位 static int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; // DFS 进行搜索,将每个岛屿标记为不同的数字 public static void dfs(int[][] grid, int x, int y, boolean[][] visited) { // 当遇到边界,直接return if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length) return; // 遇到已经访问过的或者遇到海水,直接返回 if (visited[x][y] || grid[x][y] == 0) return; visited[x][y] = true; count++; grid[x][y] = mark; // 继续向下层搜索 dfs(grid, x, y + 1, visited); dfs(grid, x, y - 1, visited); dfs(grid, x + 1, y, visited); dfs(grid, x - 1, y, visited); } public static void main (String[] args) { // 接收输入 Scanner sc = new Scanner(System.in); int m = sc.nextInt(); int n = sc.nextInt(); int[][] grid = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { grid[i][j] = sc.nextInt(); } } // 初始化mark变量,从2开始(区别于0水,1岛屿) mark = 2; // 定义二位boolean数组记录该位置是否被访问 boolean[][] visited = new boolean[m][n]; // 定义一个HashMap,记录某片岛屿的标记号和面积 HashMap<Integer, Integer> getSize = new HashMap<>(); // 定义一个HashSet,用来判断某一位置水四周是否存在不同标记编号的岛屿 Set<Integer> set = new HashSet<>(); // 定义一个boolean变量,看看DFS之后,是否全是岛屿 boolean isAllIsland = true; // 遍历二维数组进行DFS搜索,标记每片岛屿的编号,记录对应的面积 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] == 0) isAllIsland = false; if (grid[i][j] == 1) { count = 0; dfs(grid, i, j, visited); getSize.put(mark, count); mark++; } } } int result = 0; if (isAllIsland) { System.out.println(m*n); return; } // 对标记完的grid继续遍历,判断每个水位置四周是否有岛屿,并记录下四周不同相邻岛屿面积之和 // 每次计算完一个水位置周围可能存在的岛屿面积之和,更新下result变量 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] == 0) { set.clear(); // 当前水位置变更为岛屿,所以初始化为1 int curSize = 1; for (int[] dir : dirs) { int curRow = i + dir[0]; int curCol = j + dir[1]; if (curRow < 0 || curRow >= m || curCol < 0 || curCol >= n) continue; int curMark = grid[curRow][curCol]; // 如果当前相邻的岛屿已经遍历过或者HashMap中不存在这个编号,继续搜索 if (set.contains(curMark) || !getSize.containsKey(curMark)) continue; set.add(curMark); curSize += getSize.get(curMark); } result = Math.max(result, curSize); } } } // 打印结果 System.out.println(result); } }
感谢大佬分享:
代码随想录算法训练营第五十二天|Day52 图论-CSDN博客