题目解析
题目
小F被神秘力量带入了一个魔幻世界,这里危机四伏。为了在异世界中生存,小F需要找到安全区。异世界可以被表示为一个大小为 n x m 的二维数组,每个格子的值代表该位置的危险程度。
小F的能力值为 X,当某个格子的危险程度小于等于 X 时,这个格子是安全的。如果多个安全的格子相邻(上下左右连通),它们可以构成一个安全区。你需要帮助小F计算出一共有多少个安全区。
问题理解
你需要在一个 n x m 的二维数组中找到所有安全区的数量。安全区的定义是:所有相邻的格子(上下左右)的危险程度都小于等于 X。
数据结构选择
- 二维数组:用于存储每个格子的危险程度。
- 访问标记数组:用于标记哪些格子已经被访问过,避免重复计算。
算法步骤
- 初始化:创建一个与输入数组相同大小的访问标记数组,初始值为
false,表示所有格子都未被访问。 - 遍历数组:对于每个格子,如果它未被访问且危险程度小于等于
X,则从这个格子开始进行深度优先搜索(DFS)或广度优先搜索(BFS),标记所有相邻的安全格子。 - DFS/BFS:在搜索过程中,标记访问过的格子,并继续搜索其相邻的格子,直到没有未访问的安全格子为止。
- 计数:每次完成一个安全区的搜索,计数器加一。
- 返回结果:最终返回安全区的数量。
总结
通过上述步骤,你可以有效地计算出二维数组中安全区的数量。你可以选择使用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;
}