魔幻世界中的安全区计算| 青训营X豆包MarsCode 技术训练营

110 阅读6分钟

题目解析

题目

小F被神秘力量带入了一个魔幻世界,这里危机四伏。为了在异世界中生存,小F需要找到安全区。异世界可以被表示为一个大小为 n x m 的二维数组,每个格子的值代表该位置的危险程度。 小F的能力值为 X,当某个格子的危险程度小于等于 X 时,这个格子是安全的。如果多个安全的格子相邻(上下左右连通),它们可以构成一个安全区。你需要帮助小F计算出一共有多少个安全区。

问题理解

你需要在一个 n x m 的二维数组中找到所有安全区的数量。安全区的定义是:所有相邻的格子(上下左右)的危险程度都小于等于 X

数据结构选择

  1. 二维数组:用于存储每个格子的危险程度。
  2. 访问标记数组:用于标记哪些格子已经被访问过,避免重复计算。

算法步骤

  1. 初始化:创建一个与输入数组相同大小的访问标记数组,初始值为 false,表示所有格子都未被访问。
  2. 遍历数组:对于每个格子,如果它未被访问且危险程度小于等于 X,则从这个格子开始进行深度优先搜索(DFS)或广度优先搜索(BFS),标记所有相邻的安全格子。
  3. DFS/BFS:在搜索过程中,标记访问过的格子,并继续搜索其相邻的格子,直到没有未访问的安全格子为止。
  4. 计数:每次完成一个安全区的搜索,计数器加一。
  5. 返回结果:最终返回安全区的数量。

总结

通过上述步骤,你可以有效地计算出二维数组中安全区的数量。你可以选择使用DFS或BFS来实现搜索过程,具体选择哪种方法取决于你的偏好和具体实现。 希望这些思路对你有所帮助!如果你有任何具体问题或需要进一步的代码提示,请告诉我。

算法解析

DFS(深度优先遍历)

  • 对于图来说:

    • 假设初始状态是图中所有顶点均未被访问
    • 从某个顶点出发,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。
    • 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
  • 实现深度优先遍历的关键在于回溯。所谓“回溯”,就是自后往前,追溯曾经走过的路径。

算法特点

  • 深度优先搜索是一个递归的过程。

    • 首先,选定一个出发点后进行遍历,如果有邻接的未被访问过的节点则继续前进。
    • 若不能继续前进,则回退一步再前进
    • 若回退一步仍然不能前进,则连续回退至可以前进的位置为止。
    • 重复此过程,直到所有与选定点相通的所有顶点都被遍历。
  • 深度优先搜索是递归过程,带有回退操作,因此需要使用栈存储访问的路径信息。当访问到的当前顶点没有可以前进的邻接顶点时,需要进行出栈操作,将当前位置回退至出栈元素位置。

代码表示

void dfs(int i, int j, int n, int m, int X, vector<vector<int>>& a, vector<vector<bool>>& visited) {
    // 检查边界条件和是否已经访问过
    if (i < 0 || i >= n || j < 0 || j >= m || visited[i][j] || a[i][j] > X) {
        return;
    }
    
    // 标记当前格子为已访问
    visited[i][j] = true;
    
    // 递归访问相邻的格子
    dfs(i - 1, j, n, m, X, a, visited); // 上
    dfs(i + 1, j, n, m, X, a, visited); // 下
    dfs(i, j - 1, n, m, X, a, visited); // 左
    dfs(i, j + 1, n, m, X, a, visited); // 右
}

BFS(广度优先遍历)

  • 思想:

    • 从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点
    • 然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。
    • 如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。
  • 实现广度优先遍历的关键在于回放

回溯与重放是完全相反的过程。

算法特点

广度优先搜索类似于树的层次遍历,是按照一种由近及远的方式访问图的顶点。在进行广度优先搜索时需要使用队列存储顶点信息。

代码表示

void bfs(int i, int j, int n, int m, int X, vector<vector<int>>& a, vector<vector<bool>>& visited) {
    // 定义方向数组,用于访问相邻格子
    vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    queue<pair<int, int>> q;
    q.push({i, j});
    visited[i][j] = true;
    
    while (!q.empty()) {
        auto [x, y] = q.front();
        q.pop();
        
        // 访问相邻的格子
        for (auto [dx, dy] : directions) {
            int nx = x + dx;
            int ny = y + dy;
            
            // 检查边界条件和是否已经访问过
            if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && a[nx][ny] <= X) {
                visited[nx][ny] = true;
                q.push({nx, ny});
            }
        }
    }
}

结语

这个题目AI豆包给了我很大帮助,让我回忆起遍历图的两种最常用方法,在修改BFS/DFS的算法BUG中,豆包展现了极高的效率,该题目全部代码如下:

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

void bfs(int i, int j, int n, int m, int X, vector<vector<int>>& a, vector<vector<bool>>& visited) {
    // 定义方向数组,用于访问相邻格子
    vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    queue<pair<int, int>> q;
    q.push({i, j});
    visited[i][j] = true;
    
    while (!q.empty()) {
        auto [x, y] = q.front();
        q.pop();
        
        // 访问相邻的格子
        for (auto [dx, dy] : directions) {
            int nx = x + dx;
            int ny = y + dy;
            
            // 检查边界条件和是否已经访问过
            if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && a[nx][ny] <= X) {
                visited[nx][ny] = true;
                q.push({nx, ny});
            }
        }
    }
}

int solution(int n, int m, int X, vector<vector<int>>& a) {
    // 初始化访问标记数组
    vector<vector<bool>> visited(n, vector<bool>(m, false));
    int safeZones = 0;
    
    // 遍历整个数组
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            // 如果当前格子未被访问且危险程度小于等于X
            if (!visited[i][j] && a[i][j] <= X) {
                // 开始BFS搜索
                bfs(i, j, n, m, X, a, visited);
                // 完成一个安全区的搜索,计数器加一
                safeZones++;
            }
        }
    }
    
    return safeZones;
}

int main() {
    vector<vector<int>> a1 = {{2, 3, 3}, {3, 3, 3}, {3, 3, 3}};
    vector<vector<int>> a2 = {{6, 6}, {6, 4}};
    vector<vector<int>> a3 = {{1, 2, 2}, {2, 3, 3}, {3, 4, 5}};
    
    cout << (solution(3, 3, 4, a1) == 1) << endl;
    cout << (solution(2, 2, 5, a2) == 1) << endl;
    cout << (solution(3, 3, 3, a3) == 1) << endl;
    
    return 0;
}