588 lines
14 KiB
Markdown
588 lines
14 KiB
Markdown
|
||
<p align="center"><strong><a href="./qita/join.md">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益!</strong></p>
|
||
|
||
# 101. 孤岛的总面积
|
||
|
||
[卡码网:101. 孤岛的总面积](https://kamacoder.com/problempage.php?pid=1173)
|
||
|
||
题目描述
|
||
|
||
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。
|
||
|
||
|
||
现在你需要计算所有孤岛的总面积,岛屿面积的计算方式为组成岛屿的陆地的总数。
|
||
|
||
输入描述
|
||
|
||
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0。
|
||
|
||
输出描述
|
||
|
||
输出一个整数,表示所有孤岛的总面积,如果不存在孤岛,则输出 0。
|
||
|
||
输入示例
|
||
|
||
```
|
||
4 5
|
||
1 1 0 0 0
|
||
1 1 0 0 0
|
||
0 0 1 0 0
|
||
0 0 0 1 1
|
||
```
|
||
|
||
|
||
输出示例:
|
||
|
||
1
|
||
|
||
提示信息:
|
||
|
||

|
||
|
||
在矩阵中心部分的岛屿,因为没有任何一个单元格接触到矩阵边缘,所以该岛屿属于孤岛,总面积为 1。
|
||
|
||
|
||
数据范围:
|
||
|
||
1 <= M, N <= 50。
|
||
|
||
## 思路
|
||
|
||
本题使用dfs,bfs,并查集都是可以的。
|
||
|
||
本题要求找到不靠边的陆地面积,那么我们只要从周边找到陆地然后 通过 dfs或者bfs 将周边靠陆地且相邻的陆地都变成海洋,然后再去重新遍历地图 统计此时还剩下的陆地就可以了。
|
||
|
||
如图,在遍历地图周围四个边,靠地图四边的陆地,都为绿色,
|
||
|
||

|
||
|
||
在遇到地图周边陆地的时候,将1都变为0,此时地图为这样:
|
||
|
||

|
||
|
||
然后我们再去遍历这个地图,遇到有陆地的地方,去采用深搜或者广搜,边统计所有陆地。
|
||
|
||
如果对深搜或者广搜不够了解,建议先看这里:[深度优先搜索精讲](./图论深搜理论基础.md),[广度优先搜索精讲](./图论广搜理论基础.md)。
|
||
|
||
|
||
采用深度优先搜索的代码如下:
|
||
|
||
```CPP
|
||
#include <iostream>
|
||
#include <vector>
|
||
using namespace std;
|
||
int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1}; // 保存四个方向
|
||
int count; // 统计符合题目要求的陆地空格数量
|
||
void dfs(vector<vector<int>>& grid, int x, int y) {
|
||
grid[x][y] = 0;
|
||
count++;
|
||
for (int i = 0; i < 4; i++) { // 向四个方向遍历
|
||
int nextx = x + dir[i][0];
|
||
int nexty = y + dir[i][1];
|
||
// 超过边界
|
||
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
|
||
// 不符合条件,不继续遍历
|
||
if (grid[nextx][nexty] == 0) continue;
|
||
|
||
dfs (grid, nextx, nexty);
|
||
}
|
||
return;
|
||
}
|
||
|
||
int main() {
|
||
int n, m;
|
||
cin >> n >> m;
|
||
vector<vector<int>> grid(n, vector<int>(m, 0));
|
||
for (int i = 0; i < n; i++) {
|
||
for (int j = 0; j < m; j++) {
|
||
cin >> grid[i][j];
|
||
}
|
||
}
|
||
|
||
// 从左侧边,和右侧边 向中间遍历
|
||
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);
|
||
}
|
||
count = 0;
|
||
for (int i = 0; i < n; i++) {
|
||
for (int j = 0; j < m; j++) {
|
||
if (grid[i][j] == 1) dfs(grid, i, j);
|
||
}
|
||
}
|
||
cout << count << endl;
|
||
}
|
||
```
|
||
|
||
采用广度优先搜索的代码如下:
|
||
|
||
```CPP
|
||
#include <iostream>
|
||
#include <vector>
|
||
#include <queue>
|
||
using namespace std;
|
||
int count = 0;
|
||
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
|
||
void bfs(vector<vector<int>>& grid, int x, int y) {
|
||
queue<pair<int, int>> que;
|
||
que.push({x, y});
|
||
grid[x][y] = 0; // 只要加入队列,立刻标记
|
||
count++;
|
||
while(!que.empty()) {
|
||
pair<int ,int> cur = que.front(); que.pop();
|
||
int curx = cur.first;
|
||
int cury = cur.second;
|
||
for (int i = 0; i < 4; i++) {
|
||
int nextx = curx + dir[i][0];
|
||
int nexty = cury + dir[i][1];
|
||
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过
|
||
if (grid[nextx][nexty] == 1) {
|
||
que.push({nextx, nexty});
|
||
count++;
|
||
grid[nextx][nexty] = 0; // 只要加入队列立刻标记
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
int main() {
|
||
int n, m;
|
||
cin >> n >> m;
|
||
vector<vector<int>> grid(n, vector<int>(m, 0));
|
||
for (int i = 0; i < n; i++) {
|
||
for (int j = 0; j < m; j++) {
|
||
cin >> grid[i][j];
|
||
}
|
||
}
|
||
// 从左侧边,和右侧边 向中间遍历
|
||
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);
|
||
}
|
||
// 从上边和下边 向中间遍历
|
||
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);
|
||
}
|
||
}
|
||
|
||
cout << count << endl;
|
||
}
|
||
```
|
||
|
||
|
||
## 其他语言版本
|
||
|
||
### Java
|
||
|
||
``` java
|
||
|
||
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()) {
|
||
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();
|
||
}
|
||
}
|
||
|
||
// 从左侧边,和右侧边向中间遍历
|
||
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);
|
||
}
|
||
|
||
// 从上边和下边向中间遍历
|
||
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);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
```
|
||
|
||
|
||
### Python
|
||
```python
|
||
from collections import deque
|
||
|
||
# 处理输入
|
||
n, m = list(map(int, input().strip()))
|
||
g = []
|
||
for _ in range(n):
|
||
row = list(map(int, input().strip()))
|
||
g.append(row)
|
||
|
||
# 定义四个方向、孤岛面积(遍历完边缘后会被重置)
|
||
directions = [[0,1], [1,0], [-1,0], [0,-1]]
|
||
count = 0
|
||
|
||
# 广搜
|
||
def bfs(r, c):
|
||
global count
|
||
q = deque()
|
||
q.append((r, c))
|
||
g[r][c] = 0
|
||
count += 1
|
||
|
||
while q:
|
||
r, c = q.popleft()
|
||
for di in directions:
|
||
next_r = r + di[0]
|
||
next_c = c + di[1]
|
||
if next_c < 0 or next_c >= m or next_r < 0 or next_r >= n:
|
||
continue
|
||
if g[next_r][next_c] == 1:
|
||
q.append((next_r, next_c))
|
||
g[next_r][next_c] = 0
|
||
count += 1
|
||
|
||
|
||
for i in range(n):
|
||
if g[i][0] == 1: bfs(i, 0)
|
||
if g[i][m-1] == 1: bfs(i, m-1)
|
||
|
||
for i in range(m):
|
||
if g[0][i] == 1: bfs(0, i)
|
||
if g[n-1][i] == 1: bfs(n-1, i)
|
||
|
||
count = 0
|
||
for i in range(n):
|
||
for j in range(m):
|
||
if g[i][j] == 1: bfs(i, j)
|
||
|
||
print(count)
|
||
```
|
||
### Go
|
||
|
||
``` go
|
||
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
)
|
||
|
||
var count int
|
||
var dir = [4][2]int{{0, 1}, {1, 0}, {-1, 0}, {0, -1}} // 四个方向
|
||
|
||
func bfs(grid [][]int, x, y int) {
|
||
queue := [][2]int{{x, y}}
|
||
grid[x][y] = 0 // 只要加入队列,立刻标记
|
||
count++
|
||
|
||
for len(queue) > 0 {
|
||
cur := queue[0]
|
||
queue = queue[1:]
|
||
curx, cury := cur[0], cur[1]
|
||
|
||
for i := 0; i < 4; i++ {
|
||
nextx := curx + dir[i][0]
|
||
nexty := cury + dir[i][1]
|
||
|
||
if nextx < 0 || nextx >= len(grid) || nexty < 0 || nexty >= len(grid[0]) {
|
||
continue // 越界了,直接跳过
|
||
}
|
||
|
||
if grid[nextx][nexty] == 1 {
|
||
queue = append(queue, [2]int{nextx, nexty})
|
||
count++
|
||
grid[nextx][nexty] = 0 // 只要加入队列立刻标记
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func main() {
|
||
var n, m int
|
||
fmt.Scan(&n, &m)
|
||
|
||
grid := make([][]int, n)
|
||
for i := range grid {
|
||
grid[i] = make([]int, m)
|
||
}
|
||
|
||
for i := 0; i < n; i++ {
|
||
for j := 0; j < m; j++ {
|
||
fmt.Scan(&grid[i][j])
|
||
}
|
||
}
|
||
|
||
// 从左侧边,和右侧边向中间遍历
|
||
for 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)
|
||
}
|
||
}
|
||
|
||
// 从上边和下边向中间遍历
|
||
for 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 i := 0; i < n; i++ {
|
||
for j := 0; j < m; j++ {
|
||
if grid[i][j] == 1 {
|
||
bfs(grid, i, j)
|
||
}
|
||
}
|
||
}
|
||
|
||
fmt.Println(count)
|
||
}
|
||
|
||
|
||
```
|
||
|
||
### Rust
|
||
|
||
### Javascript
|
||
|
||
#### 深搜版
|
||
|
||
```javascript
|
||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||
// 创建readline接口
|
||
let iter = r1[Symbol.asyncIterator]();
|
||
// 创建异步迭代器
|
||
const readline = async () => (await iter.next()).value;
|
||
|
||
let graph // 地图
|
||
let N, M // 地图大小
|
||
let count = 0 // 孤岛的总面积
|
||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||
|
||
|
||
// 读取输入,初始化地图
|
||
const initGraph = async () => {
|
||
let line = await readline();
|
||
[N, M] = line.split(' ').map(Number);
|
||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||
|
||
for (let i = 0; i < N; i++) {
|
||
line = await readline()
|
||
line = line.split(' ').map(Number)
|
||
for (let j = 0; j < M; j++) {
|
||
graph[i][j] = line[j]
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @description: 从(x,y)开始深度优先遍历地图
|
||
* @param {*} graph 地图
|
||
* @param {*} x 开始搜索节点的下标
|
||
* @param {*} y 开始搜索节点的下标
|
||
* @return {*}
|
||
*/
|
||
const dfs = (graph, x, y) => {
|
||
if(graph[x][y] === 0) return
|
||
graph[x][y] = 0 // 标记为海洋
|
||
for (let i = 0; i < 4; i++) {
|
||
let nextx = x + dir[i][0]
|
||
let nexty = y + dir[i][1]
|
||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||
dfs(graph, nextx, nexty)
|
||
}
|
||
}
|
||
|
||
(async function () {
|
||
|
||
// 读取输入,初始化地图
|
||
await initGraph()
|
||
|
||
// 遍历地图左右两边
|
||
for (let i = 0; i < N; i++) {
|
||
if (graph[i][0] === 1) dfs(graph, i, 0)
|
||
if (graph[i][M - 1] === 1) dfs(graph, i, M - 1)
|
||
}
|
||
|
||
// 遍历地图上下两边
|
||
for (let j = 0; j < M; j++) {
|
||
if (graph[0][j] === 1) dfs(graph, 0, j)
|
||
if (graph[N - 1][j] === 1) dfs(graph, N - 1, j)
|
||
}
|
||
|
||
count = 0
|
||
// 统计孤岛的总面积
|
||
for (let i = 0; i < N; i++) {
|
||
for (let j = 0; j < M; j++) {
|
||
if (graph[i][j] === 1) count++
|
||
}
|
||
}
|
||
console.log(count);
|
||
})()
|
||
```
|
||
|
||
|
||
|
||
#### 广搜版
|
||
|
||
```javascript
|
||
const r1 = require('readline').createInterface({ input: process.stdin });
|
||
// 创建readline接口
|
||
let iter = r1[Symbol.asyncIterator]();
|
||
// 创建异步迭代器
|
||
const readline = async () => (await iter.next()).value;
|
||
|
||
let graph // 地图
|
||
let N, M // 地图大小
|
||
let count = 0 // 孤岛的总面积
|
||
const dir = [[0, 1], [1, 0], [0, -1], [-1, 0]] //方向
|
||
|
||
|
||
// 读取输入,初始化地图
|
||
const initGraph = async () => {
|
||
let line = await readline();
|
||
[N, M] = line.split(' ').map(Number);
|
||
graph = new Array(N).fill(0).map(() => new Array(M).fill(0))
|
||
|
||
for (let i = 0; i < N; i++) {
|
||
line = await readline()
|
||
line = line.split(' ').map(Number)
|
||
for (let j = 0; j < M; j++) {
|
||
graph[i][j] = line[j]
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @description: 从(x,y)开始广度优先遍历地图
|
||
* @param {*} graph 地图
|
||
* @param {*} x 开始搜索节点的下标
|
||
* @param {*} y 开始搜索节点的下标
|
||
* @return {*}
|
||
*/
|
||
const bfs = (graph, x, y) => {
|
||
let queue = []
|
||
queue.push([x, y])
|
||
graph[x][y] = 0 // 只要加入队列,立刻标记
|
||
|
||
while (queue.length) {
|
||
let [xx, yy] = queue.shift()
|
||
for (let i = 0; i < 4; i++) {
|
||
let nextx = xx + dir[i][0]
|
||
let nexty = yy + dir[i][1]
|
||
if (nextx < 0 || nextx >= N || nexty < 0 || nexty >= M) continue
|
||
if (graph[nextx][nexty] === 1) {
|
||
queue.push([nextx, nexty])
|
||
graph[nextx][nexty] = 0 // 只要加入队列,立刻标记
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
(async function () {
|
||
|
||
// 读取输入,初始化地图
|
||
await initGraph()
|
||
|
||
// 遍历地图左右两边
|
||
for (let i = 0; i < N; i++) {
|
||
if (graph[i][0] === 1) bfs(graph, i, 0)
|
||
if (graph[i][M - 1] === 1) bfs(graph, i, M - 1)
|
||
}
|
||
|
||
// 遍历地图上下两边
|
||
for (let j = 0; j < M; j++) {
|
||
if (graph[0][j] === 1) bfs(graph, 0, j)
|
||
if (graph[N - 1][j] === 1) bfs(graph, N - 1, j)
|
||
}
|
||
|
||
count = 0
|
||
// 统计孤岛的总面积
|
||
for (let i = 0; i < N; i++) {
|
||
for (let j = 0; j < M; j++) {
|
||
if (graph[i][j] === 1) count++
|
||
}
|
||
}
|
||
console.log(count);
|
||
})()
|
||
```
|
||
|
||
|
||
|
||
### TypeScript
|
||
|
||
### PhP
|
||
|
||
### Swift
|
||
|
||
### Scala
|
||
|
||
### C#
|
||
|
||
### Dart
|
||
|
||
### C
|
||
|