文章目录
题目
标题和出处
标题:使陆地分离的最少天数
难度
8 级
题目描述
要求
给定一个 m × n \texttt{m} \times \texttt{n} m×n 的二进制网格 grid \texttt{grid} grid,其中 1 \texttt{1} 1 代表陆地, 0 \texttt{0} 0 代表水域。岛屿是由相邻的 1 \texttt{1} 1 在水平方向或竖直方向上连接构成的最大组合。
如果恰好有一个岛屿 ,则认为网格是连通 的,否则网格是分离的。
一天内,可以将任何单个陆地单元( 1 \texttt{1} 1)更改为水单元( 0 \texttt{0} 0)。
返回使陆地分离的最少天数。
示例
示例 1:

输入: grid = \[0,1,1,0,0,1,1,0,0,0,0,0] \texttt{grid = \[0,1,1,0,0,1,1,0,0,0,0,0]} grid = \[0,1,1,0,0,1,1,0,0,0,0,0]
输出: 2 \texttt{2} 2
解释:至少需要 2 \texttt{2} 2 天得到分离的陆地。
将陆地 grid11 \texttt{grid11} grid11 和 grid02 \texttt{grid02} grid02 更改为水,得到两个分离的岛屿。
示例 2:

输入: grid = \[1,1] \texttt{grid = \[1,1]} grid = \[1,1]
输出: 2 \texttt{2} 2
解释:如果网格中都是水,也是分离的( \[1,1] → \[0,0] \texttt{\[1,1]} \rightarrow \texttt{\[0,0]} \[1,1]→\[0,0])。零岛屿。
数据范围
- m = grid.length \texttt{m} = \texttt{grid.length} m=grid.length
- n = gridi.length \texttt{n} = \texttt{gridi.length} n=gridi.length
- 1 ≤ m, n ≤ 30 \texttt{1} \le \texttt{m, n} \le \texttt{30} 1≤m, n≤30
- gridij \texttt{gridij} gridij 为 0 \texttt{0} 0 或 1 \texttt{1} 1
前言
如果初始时网格中没有岛屿或者有至少两个岛屿,则陆地已经分离,不需要将任何陆地单元更改为水单元,最少天数是 0 0 0。
如果初始时网格中有一个或两个陆地单元组成的岛屿,则需要将所有陆地单元更改为水单元,最少天数等于陆地单元的数目。
如果初始时网格中恰好有一个岛屿,则岛屿一定存在角落,岛屿角落的单元最多和两个陆地单元相连,将与岛屿角落相连的陆地单元更改为水单元之后,岛屿角落的单元成为独立的岛屿,此时陆地分离。因此使陆地分离的最少天数不超过 2 2 2。如果将 1 1 1 个陆地单元更改为水单元可以使陆地分离,则最少天数是 1 1 1,否则最少天数是 2 2 2。
因此,最少天数一定不超过 2 2 2。问题转化成判断是否可以通过将 1 1 1 个陆地单元更改为水单元可以使陆地分离。
根据上述分析,计算使陆地分离的最少天数的做法如下。
-
计算初始时网格中的岛屿数量,如果初始岛屿数量不等于 1 1 1,则最少天数是 0 0 0。
-
如果初始岛屿数量等于 1 1 1,则遍历每个陆地单元,分别计算将每个陆地单元更改为水单元之后的岛屿数量。如果存在一个陆地单元,将该陆地单元更改为水单元之后的岛屿数量不等于 1 1 1,则最少天数是 1 1 1。如果不存在这样的陆地单元,则最少天数是 2 2 2。
计算岛屿数量可以使用广度优先搜索或深度优先搜索实现。
解法一
思路和算法
使用广度优先搜索计算岛屿数量的做法是,依次遍历网格中的每个单元,如果遇到一个单元是陆地且状态是未访问,则遇到一个新的岛屿,将岛屿数量加 1 1 1,并使用广度优先搜索访问与当前陆地连接的所有陆地,即访问当前岛屿的所有单元。遍历结束之后,即可得到岛屿数量。
如果初始岛屿数量等于 1 1 1,则依次遍历每个陆地,对于每个陆地,将陆地更改为水之后计算岛屿数量,计算结束之后将该单元恢复成陆地,继续遍历其他的陆地。
代码
java
class Solution {
static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int m, n;
int[][] grid;
public int minDays(int[][] grid) {
this.m = grid.length;
this.n = grid[0].length;
this.grid = grid;
if (numIslands() != 1) {
return 0;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
grid[i][j] = 0;
if (numIslands() != 1) {
return 1;
}
grid[i][j] = 1;
}
}
}
return 2;
}
public int numIslands() {
int islands = 0;
boolean[][] visited = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0 || visited[i][j]) {
continue;
}
islands++;
visited[i][j] = true;
Queue<int[]> queue = new ArrayDeque<int[]>();
queue.offer(new int[]{i, j});
while (!queue.isEmpty()) {
int[] cell = queue.poll();
int row = cell[0], col = cell[1];
for (int[] dir : dirs) {
int newRow = row + dir[0], newCol = col + dir[1];
if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n && grid[newRow][newCol] == 1 && !visited[newRow][newCol]) {
visited[newRow][newCol] = true;
queue.offer(new int[]{newRow, newCol});
}
}
}
}
}
return islands;
}
}
复杂度分析
-
时间复杂度: O ( m 2 n 2 ) O(m^2n^2) O(m2n2),其中 m m m 和 n n n 分别是网格 grid \textit{grid} grid 的行数和列数。计算初始岛屿数量需要 O ( m n ) O(mn) O(mn) 的时间,对于每个陆地单元更改为水单元之后计算岛屿数量需要 O ( m n ) O(mn) O(mn) 的时间,由于陆地单元的数量是 O ( m n ) O(mn) O(mn),因此对于所有陆地单元计算更改为水单元之后的岛屿数量需要 O ( m 2 n 2 ) O(m^2n^2) O(m2n2) 的时间,总时间复杂度是 O ( m n + m 2 n 2 ) = O ( m 2 n 2 ) O(mn + m^2n^2) = O(m^2n^2) O(mn+m2n2)=O(m2n2)。
-
空间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是网格 grid \textit{grid} grid 的行数和列数。记录每个单元格是否访问过的二维数组和队列需要 O ( m n ) O(mn) O(mn) 的空间。
解法二
思路和算法
使用深度优先搜索计算岛屿数量的做法是,依次遍历网格中的每个单元,如果遇到一个单元是陆地且状态是未访问,则遇到一个新的岛屿,将岛屿数量加 1 1 1,并使用深度优先搜索访问与当前陆地连接的所有陆地,即访问当前岛屿的所有单元。遍历结束之后,即可得到岛屿数量。
如果初始岛屿数量等于 1 1 1,则依次遍历每个陆地,对于每个陆地,将陆地更改为水之后计算岛屿数量,计算结束之后将该单元恢复成陆地,继续遍历其他的陆地。
代码
java
class Solution {
static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int m, n;
int[][] grid;
boolean[][] visited;
public int minDays(int[][] grid) {
this.m = grid.length;
this.n = grid[0].length;
this.grid = grid;
if (numIslands() != 1) {
return 0;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
grid[i][j] = 0;
if (numIslands() != 1) {
return 1;
}
grid[i][j] = 1;
}
}
}
return 2;
}
public int numIslands() {
int islands = 0;
this.visited = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0 || visited[i][j]) {
continue;
}
islands++;
dfs(i, j);
}
}
return islands;
}
public void dfs(int row, int col) {
visited[row][col] = true;
for (int[] dir : dirs) {
int newRow = row + dir[0], newCol = col + dir[1];
if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n && grid[newRow][newCol] == 1 && !visited[newRow][newCol]) {
dfs(newRow, newCol);
}
}
}
}
复杂度分析
-
时间复杂度: O ( m 2 n 2 ) O(m^2n^2) O(m2n2),其中 m m m 和 n n n 分别是网格 grid \textit{grid} grid 的行数和列数。计算初始岛屿数量需要 O ( m n ) O(mn) O(mn) 的时间,对于每个陆地单元更改为水单元之后计算岛屿数量需要 O ( m n ) O(mn) O(mn) 的时间,由于陆地单元的数量是 O ( m n ) O(mn) O(mn),因此对于所有陆地单元计算更改为水单元之后的岛屿数量需要 O ( m 2 n 2 ) O(m^2n^2) O(m2n2) 的时间,总时间复杂度是 O ( m n + m 2 n 2 ) = O ( m 2 n 2 ) O(mn + m^2n^2) = O(m^2n^2) O(mn+m2n2)=O(m2n2)。
-
空间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是网格 grid \textit{grid} grid 的行数和列数。记录每个单元格是否访问过的二维数组和递归调用栈需要 O ( m n ) O(mn) O(mn) 的空间。