华为OD机试真题——战场索敌(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

发布于:2025-05-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

在这里插入图片描述

2025 A卷 100分 题型

本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式;
并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析;
本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分享

华为OD机试真题《战场索敌》:


文章快捷目录

题目描述及说明

Java

python

JavaScript

C

GO

更多内容


题目名称:战场索敌


知识点:深度优先搜索(DFS)、广度优先搜索(BFS)、逻辑处理
时间限制:1秒
空间限制:256MB
限定语言:不限


题目描述

有一个大小为N×M的战场地图,被墙壁 # 分隔成不同的区域。上下左右四个方向相邻的空地 . 属于同一区域,只有空地上可能存在敌人 E。要求统计地图中敌人数小于K的区域数量

输入描述

  • 第一行输入三个整数N、M、K,分别表示地图的行数、列数和目标敌人数阈值(N, M ≤ 100)。
  • 接下来输入一个N×M的字符数组,表示战场地图,包含字符 #(墙壁)、.(空地)、E(敌人)。

输出描述

  • 输出一个整数,表示敌人数小于K的区域数量。

示例
输入:

3 5 2  
..#EE  
E.#E.  
###..  

输出:

1  

说明

  • 地图被墙壁分为两个区域:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧区域满足条件。

输入约束

  • 输入保证每个区域的敌人数量为非负整数。
  • 任意两个敌人位置不重叠。

Java

问题分析

题目要求统计战场地图中被墙壁分隔的各个区域中,敌人数小于给定阈值的区域数量。每个区域由上下左右相邻的空地(包括敌人所在位置)组成,我们需要遍历每个区域并统计其中的敌人数。

解题思路

  1. 输入处理:读取地图的行数、列数和阈值K,然后读取地图数据。
  2. 遍历地图:使用双重循环遍历每个地图位置。
  3. 区域标记与统计:对于每个未被访问且非墙壁的位置,使用广度优先搜索(BFS)遍历整个区域,统计区域内的敌人数量。
  4. 判断条件:如果当前区域的敌人数小于K,则计入结果。
  5. 输出结果:统计符合条件的区域数量并输出。

代码实现

import java.util.Scanner;
import java.util.Queue;
import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 读取输入参数 N, M, K
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        int K = scanner.nextInt();
        scanner.nextLine(); // 跳过换行符

        // 读取战场地图数据
        char[][] grid = new char[N][M];
        for (int i = 0; i < N; i++) {
            String line = scanner.nextLine().trim();
            for (int j = 0; j < M; j++) {
                grid[i][j] = line.charAt(j);
            }
        }

        // 标记数组,记录每个位置是否被访问过
        boolean[][] visited = new boolean[N][M];
        int count = 0; // 符合条件的区域数量

        // 方向数组,用于BFS遍历四个方向
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};

        // 遍历地图中的每个位置
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                // 如果当前位置是墙壁或已被访问,跳过
                if (grid[i][j] == '#' || visited[i][j]) {
                    continue;
                }
                // BFS初始化
                Queue<int[]> queue = new LinkedList<>();
                queue.offer(new int[]{i, j});
                visited[i][j] = true;
                int cnt = 0; // 当前区域的敌人数
                // 如果当前起始位置是敌人,计数加1
                if (grid[i][j] == 'E') {
                    cnt++;
                }
                // BFS遍历
                while (!queue.isEmpty()) {
                    int[] curr = queue.poll();
                    int x = curr[0];
                    int y = curr[1];
                    // 遍历四个方向
                    for (int k = 0; k < 4; k++) {
                        int nx = x + dx[k];
                        int ny = y + dy[k];
                        // 检查新位置是否合法且未被访问,且不是墙壁
                        if (nx >= 0 && nx < N && ny >= 0 && ny < M 
                                && !visited[nx][ny] && grid[nx][ny] != '#') {
                            visited[nx][ny] = true;
                            // 如果是敌人,计数加1
                            if (grid[nx][ny] == 'E') {
                                cnt++;
                            }
                            queue.offer(new int[]{nx, ny});
                        }
                    }
                }
                // 判断当前区域的敌人数是否小于K
                if (cnt < K) {
                    count++;
                }
            }
        }

        // 输出结果
        System.out.println(count);
    }
}

代码详细解析

  1. 输入处理:通过Scanner读取输入参数和地图数据,存储到二维数组grid中。
  2. 初始化标记数组visited数组用于记录每个位置是否已被访问,避免重复处理。
  3. 方向数组dxdy数组表示上下左右四个方向的坐标变化。
  4. 遍历地图:双重循环遍历每个位置,跳过已访问或为墙壁的位置。
  5. BFS初始化:发现未被访问的非墙壁位置时,初始化队列,标记起始位置为已访问。
  6. 敌人数统计:在BFS遍历过程中,每当访问到一个新位置时,检查是否为敌人并计数。
  7. 方向遍历:对每个位置的四个方向进行处理,确保新位置合法且未被访问。
  8. 条件判断:当前区域遍历完成后,判断敌人数是否小于K,符合条件则计数。
  9. 输出结果:统计所有符合条件的区域数量并输出。

示例测试

示例1
输入:

3 5 2  
..#EE  
E.#E.  
###..  

输出:

1

说明:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧满足条件。

示例2
输入:

2 2 1  
.E  
E.  

输出:

0

说明:所有位置属于同一区域,敌人数为2,不小于1。

示例3
输入:

1 5 3  
E.E.E  

输出:

1

说明:所有位置连通,敌人数为3,等于阈值3,不满足条件。但若K=4,则输出1。

综合分析

本方案采用BFS遍历每个区域,确保每个位置仅被访问一次,时间复杂度为O(N*M),适用于最大规模为100x100的地图。BFS相比DFS更适合处理可能的深层次递归问题,避免栈溢出。代码通过标记数组和队列高效管理区域遍历,敌人数统计在访问时立即进行,确保正确性。该方法在时间复杂度和空间复杂度上均为最优,是解决此类连通区域问题的经典方案。


python

问题分析

我们需要统计战场地图中被墙壁分隔的各个区域中,敌人数小于给定阈值 K 的区域数量。每个区域由上下左右相邻的空地(包括敌人所在位置)组成,需遍历每个区域并统计其中的敌人数。


解题思路

  1. 输入处理:读取地图的行数 N、列数 M 和阈值 K,然后读取地图数据。
  2. 遍历地图:用双重循环遍历每个位置。
  3. 区域标记与统计:对每个未被访问且非墙壁的位置,使用 广度优先搜索(BFS) 遍历整个区域,统计敌人数。
  4. 判断条件:若当前区域敌人数 < K,则计入结果。
  5. 输出结果:统计并输出符合条件的区域数量。

代码实现

from collections import deque

def main():
    # 读取输入参数 N, M, K
    N, M, K = map(int, input().split())
    
    # 读取战场地图数据(处理可能的空格)
    grid = []
    for _ in range(N):
        line = input().strip()  # 去掉首尾空格或换行符
        grid.append(list(line))
    
    # 标记数组,记录每个位置是否被访问过
    visited = [[False for _ in range(M)] for _ in range(N)]
    count = 0  # 符合条件的区域数量
    
    # 方向数组,表示上下左右四个方向的坐标变化
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    
    # 遍历地图中的每个位置
    for i in range(N):
        for j in range(M):
            # 跳过已访问或墙壁的位置
            if grid[i][j] == '#' or visited[i][j]:
                continue
            
            # BFS初始化:队列和当前区域的敌人数
            queue = deque()
            queue.append((i, j))
            visited[i][j] = True
            enemy_count = 0
            
            # 如果起始位置是敌人,计数+1
            if grid[i][j] == 'E':
                enemy_count += 1
            
            # 开始BFS遍历
            while queue:
                x, y = queue.popleft()
                # 遍历四个方向
                for dx, dy in directions:
                    nx, ny = x + dx, y + dy
                    # 检查新位置是否合法
                    if 0 <= nx < N and 0 <= ny < M:
                        # 跳过已访问或墙壁
                        if not visited[nx][ny] and grid[nx][ny] != '#':
                            visited[nx][ny] = True
                            # 如果是敌人,计数+1
                            if grid[nx][ny] == 'E':
                                enemy_count += 1
                            queue.append((nx, ny))
            
            # 判断当前区域的敌人数是否小于K
            if enemy_count < K:
                count += 1
    
    # 输出结果
    print(count)

if __name__ == "__main__":
    main()

代码详细解析

  1. 输入处理

    • N, M, K = map(int, input().split()):读取第一行的三个整数。
    • 循环读取 N 行地图数据,并存储到二维列表 grid 中。
  2. 标记数组

    • visited 是一个 N×M 的布尔数组,初始化为全 False,表示每个位置是否被访问过。
  3. 方向数组

    • directions 定义四个移动方向(上、下、左、右),用于遍历相邻位置。
  4. 双重循环遍历地图

    • 外层循环遍历行 i,内层循环遍历列 j
    • 如果当前位置是墙壁 # 或已访问过,则跳过。
  5. BFS初始化

    • 使用 deque 作为队列,起始位置 (i, j) 入队,并标记为已访问。
    • enemy_count 统计当前区域的敌人数。
  6. BFS遍历

    • 从队列中取出一个位置 (x, y)
    • 遍历四个方向,计算新位置 (nx, ny)
    • 检查新位置是否合法(不越界、未被访问且不是墙壁)。
    • 如果是敌人 E,则敌人数 +1,并将新位置入队。
  7. 判断条件

    • 遍历完一个区域后,若 enemy_count < K,则符合条件的区域数 count +1

示例测试

示例1
输入:

3 5 2  
..#EE  
E.#E.  
###..  

输出:

1

说明:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧满足条件。

示例2
输入:

2 2 1  
.E  
E.  

输出:

0

说明:所有位置属于同一区域,敌人数为2,不小于1。

示例3
输入:

4 4 3  
E.##  
#.E#  
##.#  
.E.E  

输出:

2

说明:地图被分为多个区域,其中两个区域的敌人数为1和2,均小于3。


综合分析

  1. 时间复杂度O(N×M)。每个位置最多被访问一次,BFS的队列操作是常数时间。
  2. 空间复杂度O(N×M)。存储地图和标记数组需要线性空间。
  3. 算法选择
    • BFS 更适合处理二维网格的连通区域问题,因为队列操作天然适合逐层扩展。
    • 相比 DFS,BFS 不会因递归深度过大导致栈溢出,适合处理大规模数据。
  4. 实现优化
    • 使用 deque 实现队列,保证入队和出队操作的时间复杂度为 O(1)
    • 方向数组简化了代码逻辑,避免重复判断。
  5. 适用场景:所有需要统计连通区域特性的问题(如岛屿数量、区域面积等)。

JavaScript

问题分析

我们需要统计战场地图中被墙壁分隔的各个区域中,敌人数小于给定阈值 K 的区域数量。每个区域由上下左右相邻的空地(包括敌人所在位置)组成,需遍历每个区域并统计其中的敌人数。


解题思路

  1. 输入处理:读取地图的行数 N、列数 M 和阈值 K,并将地图存储为二维数组。
  2. 遍历地图:用双重循环遍历每个位置。
  3. 区域标记与统计:对每个未被访问且非墙壁的位置,使用 广度优先搜索(BFS) 遍历整个区域,统计敌人数。
  4. 判断条件:若当前区域敌人数 < K,则计入结果。
  5. 输出结果:统计并输出符合条件的区域数量。

代码实现

function main(input) {
    const lines = input.trim().split('\n').map(line => line.trim());
    const [N, M, K] = lines[0].split(' ').map(Number);
    const grid = [];
    for (let i = 1; i <= N; i++) {
        grid.push(lines[i].split(''));
    }

    const visited = Array.from({ length: N }, () => Array(M).fill(false));
    let count = 0;
    const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];

    for (let i = 0; i < N; i++) {
        for (let j = 0; j < M; j++) {
            if (grid[i][j] === '#' || visited[i][j]) continue;

            const queue = [[i, j]];
            visited[i][j] = true;
            let enemyCount = grid[i][j] === 'E' ? 1 : 0;

            while (queue.length > 0) {
                const [x, y] = queue.shift();
                for (const [dx, dy] of directions) {
                    const nx = x + dx;
                    const ny = y + dy;
                    if (nx >= 0 && nx < N && ny >= 0 && ny < M) {
                        if (!visited[nx][ny] && grid[nx][ny] !== '#') {
                            visited[nx][ny] = true;
                            if (grid[nx][ny] === 'E') enemyCount++;
                            queue.push([nx, ny]);
                        }
                    }
                }
            }

            if (enemyCount < K) count++;
        }
    }

    console.log(count);
}

// 测试输入示例
const input1 = `
3 5 2
..#EE
E.#E.
###..
`;
main(input1); // 输出: 1

const input2 = `
2 2 1
.E
E.
`;
main(input2); // 输出: 0

const input3 = `
4 4 3
E.##
#.E#
##.#
.E.E
`;
main(input3); // 输出: 2

代码详细解析

  1. 输入处理

    • input.trim() 去除首尾空格或换行符。
    • split('\n') 按行分割输入。
    • lines[0] 提取第一行,分割出 NMK
    • 后续行存入 grid 二维数组,表示地图。
  2. 初始化标记数组

    • visited 是一个 N×M 的布尔数组,初始化为 false,记录是否访问过某个位置。
  3. 方向数组

    • directions 定义上下左右四个方向,用于遍历相邻位置。
  4. 双重循环遍历地图

    • 外层循环遍历行 i,内层循环遍历列 j
    • 若当前是墙壁 # 或已访问,跳过。
  5. BFS初始化

    • 队列初始化为当前坐标 [i, j],并标记为已访问。
    • enemyCount 初始化为当前坐标是否为敌人(E)的计数值。
  6. BFS遍历

    • 从队列中取出坐标 [x, y]
    • 遍历四个方向,计算新坐标 [nx, ny]
    • 检查新坐标是否合法(不越界、未被访问、不是墙壁)。
    • 若合法,标记为已访问,并统计敌人数量。
  7. 判断条件

    • 遍历完一个区域后,若敌人数 < K,则结果 count 加一。

示例测试

示例1
输入:

3 5 2
..#EE
E.#E.
###..

输出:

1

说明:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧符合条件。

示例2
输入:

2 2 1
.E
E.

输出:

0

说明:整个区域敌人总数为2,不小于1。

示例3
输入:

4 4 3
E.##
#.E#
##.#
.E.E

输出:

2

说明:两个区域的敌人数分别为1和2,均符合条件。


综合分析

  1. 时间复杂度O(N×M)。每个位置最多被访问一次,BFS队列操作总体复杂度为线性。
  2. 空间复杂度O(N×M)。存储地图和标记数组需要线性空间。
  3. 算法选择
    • BFS 适合处理二维网格的连通区域问题,避免递归栈溢出。
    • 队列操作通过 shiftpush 实现,虽然 shiftO(n),但数据规模较小,性能可接受。
  4. 实现优化
    • 立即标记已访问的格子,避免重复入队。
    • 方向数组简化了代码逻辑,提高可读性。
  5. 适用场景:所有需要统计连通区域特性的问题(如岛屿数量、区域面积等)。

C++

问题分析

我们需要统计战场地图中被墙壁分隔的各个区域中,敌人数小于给定阈值 K 的区域数量。每个区域由上下左右相邻的空地(包括敌人所在位置)组成,需遍历每个区域并统计其中的敌人数。


解题思路

  1. 输入处理:读取地图的行数 N、列数 M 和阈值 K,并将地图存储为二维数组。
  2. 遍历地图:用双重循环遍历每个位置。
  3. 区域标记与统计:对每个未被访问且非墙壁的位置,使用 广度优先搜索(BFS) 遍历整个区域,统计敌人数。
  4. 判断条件:若当前区域敌人数 < K,则计入结果。
  5. 输出结果:统计并输出符合条件的区域数量。

代码实现

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int main() {
    int N, M, K;
    cin >> N >> M >> K;
    
    // 读取地图数据
    vector<string> grid(N);
    for (int i = 0; i < N; ++i) {
        cin >> grid[i];
    }
    
    // 标记数组,记录每个位置是否被访问过
    vector<vector<bool>> visited(N, vector<bool>(M, false));
    int count = 0; // 符合条件的区域数量
    
    // 方向数组,表示上下左右四个方向的坐标变化
    const int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    
    // 遍历地图中的每个位置
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < M; ++j) {
            // 跳过已访问或墙壁的位置
            if (grid[i][j] == '#' || visited[i][j]) {
                continue;
            }
            
            // BFS初始化:队列和当前区域的敌人数
            queue<pair<int, int>> q;
            q.emplace(i, j);
            visited[i][j] = true;
            int enemy_count = 0;
            
            // 如果起始位置是敌人,计数+1
            if (grid[i][j] == 'E') {
                enemy_count++;
            }
            
            // 开始BFS遍历
            while (!q.empty()) {
                auto [x, y] = q.front();
                q.pop();
                
                // 遍历四个方向
                for (const auto& d : dirs) {
                    int nx = x + d[0];
                    int ny = y + d[1];
                    
                    // 检查新位置是否合法
                    if (nx >= 0 && nx < N && ny >= 0 && ny < M) {
                        // 跳过已访问或墙壁
                        if (!visited[nx][ny] && grid[nx][ny] != '#') {
                            visited[nx][ny] = true;
                            // 如果是敌人,计数+1
                            if (grid[nx][ny] == 'E') {
                                enemy_count++;
                            }
                            q.emplace(nx, ny);
                        }
                    }
                }
            }
            
            // 判断当前区域的敌人数是否小于K
            if (enemy_count < K) {
                count++;
            }
        }
    }
    
    // 输出结果
    cout << count << endl;
    return 0;
}

代码详细解析

  1. 输入处理

    • cin >> N >> M >> K:读取第一行的三个整数。
    • 循环读取 N 行地图数据,存储到 vector<string> grid 中。
  2. 标记数组

    • visited 是一个 N×M 的布尔数组,初始化为全 false,表示每个位置是否被访问过。
  3. 方向数组

    • dirs 定义四个移动方向(上、下、左、右),用于遍历相邻位置。
  4. 双重循环遍历地图

    • 外层循环遍历行 i,内层循环遍历列 j
    • 如果当前位置是墙壁 # 或已访问过,则跳过。
  5. BFS初始化

    • 使用 queue<pair<int, int>> 作为队列,起始位置 (i, j) 入队,并标记为已访问。
    • enemy_count 统计当前区域的敌人数。
  6. BFS遍历

    • 从队列中取出一个位置 (x, y)
    • 遍历四个方向,计算新位置 (nx, ny)
    • 检查新位置是否合法(不越界、未被访问且不是墙壁)。
    • 如果是敌人 E,则敌人数 +1,并将新位置入队。
  7. 判断条件

    • 遍历完一个区域后,若 enemy_count < K,则符合条件的区域数 count +1

示例测试

示例1
输入:

3 5 2
..#EE
E.#E.
###..

输出:

1

说明:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧符合条件。

示例2
输入:

2 2 1
.E
E.

输出:

0

说明:整个区域敌人总数为2,不小于1。

示例3
输入:

4 4 3
E.##
#.E#
##.#
.E.E

输出:

2

说明:两个区域的敌人数分别为1和2,均符合条件。


综合分析

  1. 时间复杂度O(N×M)。每个位置最多被访问一次,BFS队列的总体操作次数为线性。
  2. 空间复杂度O(N×M)。存储地图和标记数组需要线性空间。
  3. 算法选择
    • BFS 适合处理二维网格的连通区域问题,避免递归栈溢出。
    • 队列操作天然适合逐层扩展,确保每个区域被完整遍历。
  4. 实现优化
    • 使用 emplace 直接构造 pair,避免不必要的拷贝。
    • 方向数组简化了代码逻辑,提高可读性。
  5. 适用场景:所有需要统计连通区域特性的问题(如岛屿数量、区域面积等)。

C

问题分析

我们需要统计战场地图中被墙壁分隔的各个区域中,敌人数小于给定阈值 K 的区域数量。每个区域由上下左右相邻的空地(包括敌人所在位置)组成,需遍历每个区域并统计其中的敌人数。


解题思路

  1. 输入处理:读取地图的行数 N、列数 M 和阈值 K,并将地图存储为二维数组。
  2. 遍历地图:用双重循环遍历每个位置。
  3. 区域标记与统计:对每个未被访问且非墙壁的位置,使用 广度优先搜索(BFS) 遍历整个区域,统计敌人数。
  4. 判断条件:若当前区域敌人数 < K,则计入结果。
  5. 输出结果:统计并输出符合条件的区域数量。

代码实现

#include <stdio.h>
#include <string.h>

#define MAX_SIZE 100

int main() {
    int N, M, K;
    char grid[MAX_SIZE][MAX_SIZE]; // 存储地图
    int visited[MAX_SIZE][MAX_SIZE] = {0}; // 标记是否访问过
    int queue_x[MAX_SIZE * MAX_SIZE], queue_y[MAX_SIZE * MAX_SIZE]; // BFS队列
    int front, rear; // 队列指针
    int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 方向数组

    // 读取输入参数
    scanf("%d %d %d", &N, &M, &K);

    // 读取地图数据
    for (int i = 0; i < N; i++) {
        scanf("%s", grid[i]);
    }

    int count = 0; // 符合条件的区域数量

    // 遍历每个位置
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            // 跳过已访问或墙壁的位置
            if (grid[i][j] == '#' || visited[i][j]) continue;

            // BFS初始化
            front = 0;
            rear = 0;
            queue_x[rear] = i;
            queue_y[rear] = j;
            rear++;
            visited[i][j] = 1;
            int enemy_count = (grid[i][j] == 'E') ? 1 : 0;

            // BFS遍历
            while (front < rear) {
                int x = queue_x[front];
                int y = queue_y[front];
                front++;

                // 遍历四个方向
                for (int d = 0; d < 4; d++) {
                    int nx = x + dirs[d][0];
                    int ny = y + dirs[d][1];

                    // 检查新坐标是否合法
                    if (nx >= 0 && nx < N && ny >= 0 && ny < M) {
                        // 跳过已访问或墙壁
                        if (!visited[nx][ny] && grid[nx][ny] != '#') {
                            visited[nx][ny] = 1;
                            if (grid[nx][ny] == 'E') enemy_count++;
                            queue_x[rear] = nx;
                            queue_y[rear] = ny;
                            rear++;
                        }
                    }
                }
            }

            // 判断当前区域的敌人数是否小于K
            if (enemy_count < K) count++;
        }
    }

    // 输出结果
    printf("%d\n", count);
    return 0;
}

代码详细解析

  1. 输入处理

    • scanf("%d %d %d", &N, &M, &K):读取第一行的三个整数。
    • 循环读取 N 行地图数据,存储到二维字符数组 grid 中。
  2. 标记数组

    • visited 是一个 N×M 的整型数组,初始化为全 0,表示每个位置是否被访问过。
  3. 方向数组

    • dirs 定义四个移动方向(上、下、左、右),用于遍历相邻位置。
  4. 双重循环遍历地图

    • 外层循环遍历行 i,内层循环遍历列 j
    • 如果当前位置是墙壁 # 或已访问过,则跳过。
  5. BFS初始化

    • 使用队列数组 queue_xqueue_y 存储坐标。
    • 起始位置 (i, j) 入队,并标记为已访问。
    • enemy_count 统计当前区域的敌人数,初始化为当前坐标是否为敌人。
  6. BFS遍历

    • 从队列中取出坐标 (x, y)
    • 遍历四个方向,计算新坐标 (nx, ny)
    • 检查新坐标是否合法(不越界、未被访问且不是墙壁)。
    • 如果是敌人 E,则敌人数 +1,并将新坐标入队。
  7. 判断条件

    • 遍历完一个区域后,若 enemy_count < K,则符合条件的区域数 count 加一。

示例测试

示例1
输入:

3 5 2
..#EE
E.#E.
###..

输出:

1

说明:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧符合条件。

示例2
输入:

2 2 1
.E
E.

输出:

0

说明:整个区域敌人总数为2,不小于1。

示例3
输入:

4 4 3
E.##
#.E#
##.#
.E.E

输出:

2

说明:两个区域的敌人数分别为1和2,均符合条件。


综合分析

  1. 时间复杂度O(N×M)。每个位置最多被访问一次,BFS队列的总体操作次数为线性。
  2. 空间复杂度O(N×M)。存储地图和标记数组需要线性空间。
  3. 算法选择
    • BFS 适合处理二维网格的连通区域问题,避免递归栈溢出。
    • 队列操作天然适合逐层扩展,确保每个区域被完整遍历。
  4. 实现优化
    • 使用数组模拟队列,避免动态内存分配。
    • 立即标记已访问的格子,避免重复入队。
  5. 适用场景:所有需要统计连通区域特性的问题(如岛屿数量、区域面积等)。

GO

问题分析

我们需要统计战场地图中被墙壁分隔的各个区域中,敌人数小于给定阈值 K 的区域数量。每个区域由上下左右相邻的空地(包括敌人所在位置)组成,需遍历每个区域并统计其中的敌人数。


解题思路

  1. 输入处理:读取地图的行数 N、列数 M 和阈值 K,并将地图存储为二维数组。
  2. 遍历地图:用双重循环遍历每个位置。
  3. 区域标记与统计:对每个未被访问且非墙壁的位置,使用 广度优先搜索(BFS) 遍历整个区域,统计敌人数。
  4. 判断条件:若当前区域敌人数 < K,则计入结果。
  5. 输出结果:统计并输出符合条件的区域数量。

代码实现

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 读取输入参数
	var N, M, K int
	fmt.Scanf("%d %d %d\n", &N, &M, &K)

	// 初始化地图和标记数组
	scanner := bufio.NewScanner(os.Stdin)
	grid := make([][]byte, N)
	visited := make([][]bool, N)
	for i := 0; i < N; i++ {
		scanner.Scan()
		line := scanner.Text()
		grid[i] = []byte(line)
		visited[i] = make([]bool, M)
	}

	count := 0 // 符合条件的区域数量
	dirs := [4][2]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} // 方向数组

	// 遍历每个位置
	for i := 0; i < N; i++ {
		for j := 0; j < M; j++ {
			// 跳过已访问或墙壁的位置
			if grid[i][j] == '#' || visited[i][j] {
				continue
			}

			// BFS初始化
			queue := [][2]int{{i, j}}
			visited[i][j] = true
			enemyCount := 0

			// 统计起始位置的敌人
			if grid[i][j] == 'E' {
				enemyCount++
			}

			// BFS遍历
			for len(queue) > 0 {
				x, y := queue[0][0], queue[0][1]
				queue = queue[1:] // 弹出队列头部

				// 遍历四个方向
				for _, d := range dirs {
					nx, ny := x+d[0], y+d[1]
					// 检查新坐标是否合法
					if nx >= 0 && nx < N && ny >= 0 && ny < M {
						// 跳过已访问或墙壁
						if !visited[nx][ny] && grid[nx][ny] != '#' {
							visited[nx][ny] = true
							if grid[nx][ny] == 'E' {
								enemyCount++
							}
							queue = append(queue, [2]int{nx, ny})
						}
					}
				}
			}

			// 判断是否满足条件
			if enemyCount < K {
				count++
			}
		}
	}

	// 输出结果
	fmt.Println(count)
}

代码详细解析

  1. 输入处理

    • fmt.Scanf 读取第一行的三个整数 NMK
    • bufio.Scanner 逐行读取地图数据,存储到二维字节数组 grid 中。
  2. 初始化

    • visited 是一个 N×M 的布尔数组,初始化为 false,标记已访问的位置。
  3. 方向数组

    • dirs 定义四个移动方向(上、下、左、右),用于遍历相邻位置。
  4. 双重循环遍历地图

    • 外层循环遍历行 i,内层循环遍历列 j
    • 跳过已访问或墙壁的位置。
  5. BFS初始化

    • 队列初始化为包含起始坐标 [i, j],并标记为已访问。
    • enemyCount 统计当前区域的敌人数,起始位置为敌人则初始化为 1。
  6. BFS遍历

    • 从队列头部取出坐标 (x, y)
    • 遍历四个方向,计算新坐标 (nx, ny)
    • 若新坐标合法且未被访问,则标记为已访问,并统计敌人数量。
  7. 判断条件

    • 遍历完一个区域后,若敌人数 < K,则 count 加一。

示例测试

示例1
输入:

3 5 2
..#EE
E.#E.
###..

输出:

1

说明:左侧区域有1个敌人,右侧区域有3个敌人,仅左侧符合条件。

示例2
输入:

2 2 1
.E
E.

输出:

0

说明:整个区域敌人总数为2,不小于1。

示例3
输入:

4 4 3
E.##
#.E#
##.#
.E.E

输出:

2

说明:两个区域的敌人数分别为1和2,均符合条件。


综合分析

  1. 时间复杂度O(N×M),每个位置最多被访问一次。
  2. 空间复杂度O(N×M),存储地图和标记数组。
  3. 算法选择
    • BFS 天然适合处理网格连通区域问题,避免递归栈溢出。
    • 使用队列逐层扩展,确保每个区域被完整遍历。
  4. 实现优化
    • slice 模拟队列,避免动态内存分配。
    • 立即标记已访问的格子,避免重复入队。
  5. 适用场景:所有需要统计连通区域特性的问题(如岛屿数量、区域面积等)。

更多内容:

https://www.kdocs.cn/l/cvk0eoGYucWA

本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!


网站公告

今日签到

点亮在社区的每一天
去签到