题解 | #挡住洪水#
挡住洪水
https://www.nowcoder.com/practice/56e54f4c2e3c4a58abfe76dbc1da1d7e
题目链接
题目描述
给定一个 的字符矩阵作为地图,其中
'*'
代表围墙,'0'
代表空地。所有四方向(上下左右)连通的 '0'
构成一个区域。
如果一个区域四周都被围墙或地图边界所包围,那么洪水就无法进入,该区域被视为安全区域。请统计地图中所有安全区域包含的 '0'
单元格的总数。
解题思路 (最终修正版)
本题的真实意图是计算所有不与地图边界连通的 '0'
单元格的总数。一个区域只要有任何一个 '0'
单元格在地图的边界上,那么该区域的所有 '0'
单元格都视为不安全。
正确的解法依然是采用逆向思维:
-
识别并标记所有不安全的单元格:
首先,遍历地图的所有边界(第一行、最后一行、第一列、最后一列)。
一旦在边界上发现一个
'0'
单元格,就说明它以及它所属的整个连通区域都是不安全的。我们从这个边界的
'0'
开始进行广度优先搜索 (BFS) 或深度优先搜索 (DFS),将所有能访问到的'0'
单元格都进行标记(例如,修改为'#'
),表示它们是“不安全”的。 -
统计剩余的单元格:
完成第一步后,所有不安全的
'0'
单元格都已被标记为'#'
。现在,我们再次完整地遍历一次地图。
统计地图中仍然保持为
'0'
的单元格的数量。这个数量就是所有安全区域的单元格总数。
这个思路首先排除所有不满足条件的单元格,然后对剩下的单元格进行简单的计数,能够正确地解决问题。
代码
#include <iostream>
#include <vector>
#include <string>
#include <queue>
using namespace std;
int n, m;
vector<string> grid;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
void bfs(int start_x, int start_y) {
if (grid[start_x][start_y] != '0') {
return;
}
queue<pair<int, int>> q;
q.push({start_x, start_y});
grid[start_x][start_y] = '#';
while (!q.empty()) {
pair<int, int> curr = q.front();
q.pop();
for (int i = 0; i < 4; ++i) {
int nx = curr.first + dx[i];
int ny = curr.second + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[nx][ny] == '0') {
grid[nx][ny] = '#';
q.push({nx, ny});
}
}
}
}
int main() {
cin >> n >> m;
grid.resize(n);
for (int i = 0; i < n; ++i) {
cin >> grid[i];
}
// 1. 从边界开始,淹没所有不安全的 '0' 区域
for (int j = 0; j < m; ++j) {
if (grid[0][j] == '0') bfs(0, j);
if (n > 1 && grid[n - 1][j] == '0') bfs(n - 1, j);
}
for (int i = 1; i < n - 1; ++i) {
if (grid[i][0] == '0') bfs(i, 0);
if (m > 1 && grid[i][m - 1] == '0') bfs(i, m - 1);
}
// 2. 统计图中剩余的 '0' 单元格总数
int safe_cells = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (grid[i][j] == '0') {
safe_cells++;
}
}
}
cout << safe_cells << endl;
return 0;
}
import java.util.Scanner;
import java.util.Queue;
import java.util.LinkedList;
public class Main {
static int n, m;
static char[][] grid;
static int[] dx = {0, 0, 1, -1};
static int[] dy = {1, -1, 0, 0};
public static void bfs(int startX, int startY) {
if (grid[startX][startY] != '0') {
return;
}
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{startX, startY});
grid[startX][startY] = '#';
while (!queue.isEmpty()) {
int[] curr = queue.poll();
int x = curr[0];
int y = curr[1];
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[nx][ny] == '0') {
grid[nx][ny] = '#';
queue.offer(new int[]{nx, ny});
}
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
grid = new char[n][m];
for (int i = 0; i < n; i++) {
grid[i] = sc.next().toCharArray();
}
// 1. 从边界开始,淹没所有不安全的 '0' 区域
for (int j = 0; j < m; j++) {
if (grid[0][j] == '0') bfs(0, j);
if (n > 1 && grid[n - 1][j] == '0') bfs(n - 1, j);
}
for (int i = 1; i < n - 1; i++) {
if (grid[i][0] == '0') bfs(i, 0);
if (m > 1 && grid[i][m - 1] == '0') bfs(i, m - 1);
}
// 2. 统计图中剩余的 '0' 单元格总数
int safeCells = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '0') {
safeCells++;
}
}
}
System.out.println(safeCells);
}
}
import sys
from collections import deque
def solve():
n_m_str = input()
n, m = map(int, n_m_str.split())
grid = [list(input()) for _ in range(n)]
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
def bfs(start_x, start_y):
if grid[start_x][start_y] != '0':
return
q = deque([(start_x, start_y)])
grid[start_x][start_y] = '#'
while q:
x, y = q.popleft()
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < n and 0 <= ny < m and grid[nx][ny] == '0':
grid[nx][ny] = '#'
q.append((nx, ny))
# 1. 从边界开始,淹没所有不安全的 '0' 区域
for j in range(m):
if grid[0][j] == '0':
bfs(0, j)
if n > 1 and grid[n - 1][j] == '0':
bfs(n - 1, j)
for i in range(1, n - 1):
if grid[i][0] == '0':
bfs(i, 0)
if m > 1 and grid[i][m - 1] == '0':
bfs(i, m - 1)
# 2. 统计图中剩余的 '0' 单元格总数
safe_cells = 0
for r in range(n):
for c in range(m):
if grid[r][c] == '0':
safe_cells += 1
print(safe_cells)
solve()
算法及复杂度
- 算法:广度优先搜索 (BFS)
- 时间复杂度:
。我们首先通过 BFS 标记所有不安全的单元格,这会访问每个单元格至多一次。然后我们再次遍历整个网格进行计数。总时间复杂度为
。
- 空间复杂度:
。在最坏的情况下,BFS 的队列可能会存储所有单元格的坐标。