加载中...
827-最大人工岛(Making A Large Island)
发表于:2021-12-03 | 分类: 困难
字数统计: 344 | 阅读时长: 1分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/making-a-large-island

英文原文

You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1.

Return the size of the largest island in grid after applying this operation.

An island is a 4-directionally connected group of 1s.

 

Example 1:

Input: grid = [[1,0],[0,1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.

Example 2:

Input: grid = [[1,1],[1,0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 4.

Example 3:

Input: grid = [[1,1],[1,1]]
Output: 4
Explanation: Can't change any 0 to 1, only one island with area = 4.

 

Constraints:

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 500
  • grid[i][j] is either 0 or 1.

中文题目

给你一个大小为 n x n 二进制矩阵 grid最多 只能将一格 0 变成 1

返回执行此操作后,grid 中最大的岛屿面积是多少?

岛屿 由一组上、下、左、右四个方向相连的 1 形成。

 

示例 1:

输入: grid = [[1, 0], [0, 1]]
输出: 3
解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。

示例 2:

输入: grid = [[1, 1], [1, 0]]
输出: 4
解释: 将一格0变成1,岛屿的面积扩大为 4。

示例 3:

输入: grid = [[1, 1], [1, 1]]
输出: 4
解释: 没有0可以让我们变成1,面积依然为 4。

 

提示:

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 500
  • grid[i][j]01

通过代码

官方题解

方法 1:深度优先搜索【超时】

想法

对于每个 0,我们将它暂时变成 1,然后统计这个连通块的大小。

算法

对于每个 0,将它变成 1,然后做一次深度优先搜索计算出连通块的大小。答案就是找到的最大连通块。

当然,如果没有 0,那么答案就是整个网格的大小。

[]
class Solution { int[] dr = new int[]{-1, 0, 1, 0}; int[] dc = new int[]{0, -1, 0, 1}; public int largestIsland(int[][] grid) { int N = grid.length; int ans = 0; boolean hasZero = false; for (int r = 0; r < N; ++r) for (int c = 0; c < N; ++c) if (grid[r][c] == 0) { hasZero = true; grid[r][c] = 1; ans = Math.max(ans, check(grid, r, c)); grid[r][c] = 0; } return hasZero ? ans : N*N; } public int check(int[][] grid, int r0, int c0) { int N = grid.length; Stack<Integer> stack = new Stack(); Set<Integer> seen = new HashSet(); stack.push(r0 * N + c0); seen.add(r0 * N + c0); while (!stack.isEmpty()) { int code = stack.pop(); int r = code / N, c = code % N; for (int k = 0; k < 4; ++k) { int nr = r + dr[k], nc = c + dc[k]; if (!seen.contains(nr * N + nc) && 0 <= nr && nr < N && 0 <= nc && nc < N && grid[nr][nc] == 1) { stack.push(nr * N + nc); seen.add(nr * N + nc); } } } return seen.size(); } }
[]
class Solution(object): def largestIsland(self, grid): N = len(grid) def check(r, c): seen = {(r, c)} stack = [(r, c)] while stack: r, c = stack.pop() for nr, nc in ((r-1, c), (r, c-1), (r+1, c), (r, c+1)): if (nr, nc) not in seen and 0 <= nr < N and 0 <= nc < N and grid[nr][nc]: stack.append((nr, nc)) seen.add((nr, nc)) return len(seen) ans = 0 has_zero = False for r, row in enumerate(grid): for c, val in enumerate(row): if val == 0: has_zero = True grid[r][c] = 1 ans = max(ans, check(r, c)) grid[r][c] = 0 return ans if has_zero else N*N

复杂度分析

  • 时间复杂度:$O(N^4)$,其中 $N$ 是 grid 的长和宽。
  • 空间复杂度:$O(N^2)$,深度优先搜索需要的 stackseen 的空间。

方法 2:连通块大小【通过】

想法

再上一个解法中,我们检查了每个 0。然而,我们也计算了每个组的大小,所以其实并不需要利用深度优先搜索重复计算所有的连通块。

然而,这个方法会失败如果 0 和相同的连通块相交。例如,考虑 grid = [[0,1],[1,1]] 答案是 4 而不是 1 + 3 + 3,因为 0 右边的邻居和底下的邻居属于同一连通块。

我们可以通过记录连通块编号来解决这个问题,不同的连通块编号不同。这样,我们就可以累加不同编号的连通块面积和。

算法

对于每个连通块,将所有格点赋值为 index 并记录他们的大小 area[index] = dfs(...)

然后对于每个 0,查看周围的邻居编号在 seen 并将这个区域的大小加入结果,改变 seen 的值。这就是当前节点的面积大小,在其中找到最大的。

为了解决没有 0 的情况,我们只需要记录之前计算的最大面积并输出即可。

[]
class Solution { int[] dr = new int[]{-1, 0, 1, 0}; int[] dc = new int[]{0, -1, 0, 1}; int[][] grid; int N; public int largestIsland(int[][] grid) { this.grid = grid; N = grid.length; int index = 2; int[] area = new int[N*N + 2]; for (int r = 0; r < N; ++r) for (int c = 0; c < N; ++c) if (grid[r][c] == 1) area[index] = dfs(r, c, index++); int ans = 0; for (int x: area) ans = Math.max(ans, x); for (int r = 0; r < N; ++r) for (int c = 0; c < N; ++c) if (grid[r][c] == 0) { Set<Integer> seen = new HashSet(); for (Integer move: neighbors(r, c)) if (grid[move / N][move % N] > 1) seen.add(grid[move / N][move % N]); int bns = 1; for (int i: seen) bns += area[i]; ans = Math.max(ans, bns); } return ans; } public int dfs(int r, int c, int index) { int ans = 1; grid[r][c] = index; for (Integer move: neighbors(r, c)) { if (grid[move / N][move % N] == 1) { grid[move / N][move % N] = index; ans += dfs(move / N, move % N, index); } } return ans; } public List<Integer> neighbors(int r, int c) { List<Integer> ans = new ArrayList(); for (int k = 0; k < 4; ++k) { int nr = r + dr[k]; int nc = c + dc[k]; if (0 <= nr && nr < N && 0 <= nc && nc < N) ans.add(nr * N + nc); } return ans; } }
[]
class Solution(object): def largestIsland(self, grid): N = len(grid) def neighbors(r, c): for nr, nc in ((r-1, c), (r+1, c), (r, c-1), (r, c+1)): if 0 <= nr < N and 0 <= nc < N: yield nr, nc def dfs(r, c, index): ans = 1 grid[r][c] = index for nr, nc in neighbors(r, c): if grid[nr][nc] == 1: ans += dfs(nr, nc, index) return ans area = {} index = 2 for r in xrange(N): for c in xrange(N): if grid[r][c] == 1: area[index] = dfs(r, c, index) index += 1 ans = max(area.values() or [0]) for r in xrange(N): for c in xrange(N): if grid[r][c] == 0: seen = {grid[nr][nc] for nr, nc in neighbors(r, c) if grid[nr][nc] > 1} ans = max(ans, 1 + sum(area[i] for i in seen)) return ans

复杂度分析

  • 时间复杂度:$O(N^2)$,其中 $N$ 是 grid 的长度和宽度。
  • 空间复杂度:$O(N^2)$,深度优先搜索需要的数组 area 的额外空间。

统计信息

通过次数 提交次数 AC比率
10106 25850 39.1%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
828-统计子串中的唯一字符(Count Unique Characters of All Substrings of a Given String)
下一篇:
829-连续整数求和(Consecutive Numbers Sum)
本文目录
本文目录