1329:【例8.2】细胞
解决这个问题的思路涉及以下关键步骤:
-
理解题目:题目要求我们在一个数字矩阵中识别和计数由1到9数字组成的“细胞”群。一个细胞群是由相邻(上下左右)的非零数字构成的集合。需要注意的是,0不被视为细胞的一部分。
-
遍历矩阵:从矩阵的左上角开始,逐行遍历矩阵的每个元素。对于每个遍历到的非零元素(即细胞),检查是否已经被访问过。如果没有访问过,这意味着我们发现了一个新的细胞群。
-
广度优先搜索(BFS):当我们遇到一个未访问的非零元素时,使用广度优先搜索算法来遍历这个细胞群的所有成员。BFS通过将当前细胞的所有未访问邻居加入到队列中来工作,然后逐个处理队列中的元素,直到队列为空。这个过程中,每访问一个细胞,就将其标记为已访问。
- 初始化队列:将当前未访问的细胞加入到队列中。
- 处理队列:从队列中取出一个细胞,然后查找它的所有未访问邻居(上下左右四个方向)。对于每个未访问的邻居,将其加入队列并标记为已访问。
-
计数:每当通过BFS启动一次新的搜索时,这意味着我们找到了一个新的细胞群。因此,我们需要增加细胞群的计数。
-
避免重复:为了避免重复计算相同的细胞群,需要有一个机制来标记已经访问过的细胞。这通常通过一个与原矩阵同样大小的布尔矩阵来实现,其中
true
表示已访问,false
表示未访问。 -
输出结果:完成矩阵的遍历和所有可能的BFS搜索后,输出计数的细胞群数量。
整个解决方案的核心在于广度优先搜索的有效实现,它允许我们准确地遍历和标记构成每个独立细胞群的所有细胞,从而能够准确计数。
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Point {
int x, y;
};
// 定义四个方向的一维数组
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
// 广度优先搜索
void bfs(int startX, int startY, int n, int m, vector<vector<int>>& matrix, vector<vector<bool>>& visited) {
queue<Point> q;
q.push({startX, startY});
visited[startX][startY] = true;
while (!q.empty()) {
Point cell = q.front();
q.pop();
for (int i = 0; i < 4; ++i) {
int newX = cell.x + dx[i];
int newY = cell.y + dy[i];
// 将isValid函数的逻辑内联到此处
if (newX >= 0 && newX < n && newY >= 0 && newY < m && matrix[newX][newY] != 0 && !visited[newX][newY]) {
visited[newX][newY] = true;
q.push({newX, newY});
}
}
}
}
int countCells(int n, int m, vector<vector<int>>& matrix) {
vector<vector<bool>> visited(n, vector<bool>(m, false));
int count = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (matrix[i][j] != 0 && !visited[i][j]) {
bfs(i, j, n, m, matrix, visited);
++count; // 每次启动BFS时计数加一
}
}
}
return count;
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> matrix(n, vector<int>(m));
// 读取输入矩阵
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
char ch;
cin >> ch;
matrix[i][j] = ch - '0';
}
}
cout << countCells(n, m, matrix) << endl;
return 0;
}
1330:【例8.3】最少步数
为了解决这个问题,我们可以使用广度优先搜索(BFS)来找出从两个给定的点(A 和 B)到达目标点(1,1)的最短路径。由于棋子有两种走法:“日”字形和“田”字形,我们需要考虑这两种移动方式对应的坐标变化。
“日”字形的移动可以表示为:
- (x+2, y+1)
- (x+2, y-1)
- (x-2, y+1)
- (x-2, y-1)
- (x+1, y+2)
- (x+1, y-2)
- (x-1, y+2)
- (x-1, y-2)
“田”字形的移动可以表示为:
- (x+1, y+1)
- (x+1, y-1)
- (x-1, y+1)
- (x-1, y-1)
我们可以结合这两种移动方式,对每个点执行BFS,直到到达(1,1)点。在BFS过程中,我们记录每一步的步数,并在到达(1,1)点时返回该步数作为结果。
以下是C++代码示例:
#include <bits/stdc++.h>
using namespace std;
struct Node {
int x, y, dist;
};
int fx[12] = {-1, -2, -2, -1, 1, 2, 2, 1, -2, -2, 2, 2};
int fy[12] = {-2, -1, 1, 2, 2, 1, -1, -2, 2, -2, -2, 2};
int bfs(int s, int e) {
// 使用vector<vector<bool>>定义vis数组,并初始化为false
vector<vector<bool>> vis(110, vector<bool>(110, false));
queue<Node> q;
q.push({s, e, 0});
vis[s][e] = true;
while (!q.empty()) {
Node current = q.front();
q.pop();
if (current.x == 1 && current.y == 1) {
return current.dist;
}
for (int i = 0; i < 12; i++) {
int tx = current.x + fx[i];
int ty = current.y + fy[i];
if (tx >= 1 && tx <= 100 && ty >= 1 && ty <= 100 && !vis[tx][ty]) {
vis[tx][ty] = true;
q.push({tx, ty, current.dist + 1});
}
}
}
return -1; // 如果无法到达(1,1),返回-1
}
int main() {
int ax, ay, bx, by;
// 读取A、B两点的坐标
cin >> ax >> ay >> bx >> by;
// 分别计算从A、B到(1,1)的最少步数并输出
cout << bfs(ax, ay) << endl;
cout << bfs(bx, by) << endl;
return 0;
}
这段代码实现了从两个不同的起始点到达(1,1)点的最短路径计算。首先,我们定义了一个结构体Position
来存储每个位置的坐标和到达该位置所需的步数。然后,我们通过BFS遍历棋盘,直到找到从起始点到(1,1)点的最短路径。在遍历的过程中,我们使用一个visited
数组来避免重复访问同一个位置。
1248:Dungeon Master
为了解决这个三维迷宫问题并使用char
类型的vector
,我们首先定义迷宫的三维数组表示,然后通过广度优先搜索(BFS)找到从起点S
到终点E
的最短路径。下面是如何用C++实现这个解法:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int N, M; // 全局变量,表示土地的行数和列数
// 定义表示点的结构体
struct Node {
int x, y;
};
// 八个方向:上、下、左、右以及四个对角线方向
int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
void bfs(vector<vector<char>>& land, int x, int y) {
queue<Node> q;
q.push({x, y});
land[x][y] = '.'; // 标记为已访问
while (!q.empty()) {
Node current = q.front();
q.pop();
for (int i = 0; i < 8; ++i) {
int nx = current.x + dx[i], ny = current.y + dy[i];
if (nx >= 0 && nx < N && ny >= 0 && ny < M && land[nx][ny] == 'W') {
land[nx][ny] = '.'; // 标记为已访问
q.push({nx, ny});
}
}
}
}
int main() {
cin >> N >> M;
vector<vector<char>> land(N, vector<char>(M));
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
cin >> land[i][j];
int puddles = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (land[i][j] == 'W') {
bfs(land, i, j); // 从有水的地方开始BFS
++puddles; // 找到一个水洼
}
}
}
cout << puddles << endl; // 输出水洼总数
return 0;
}
这段代码首先定义了一个三维vector
来表示迷宫,每个元素是一个char
,代表迷宫中的一个单元格。Node
结构体用于BFS中,表示当前位置和从起点到该点的距离(以分钟为单位)。bfs
函数实现了广度优先搜索算法,以找到从起点到终点的最短路径。如果找到了路径,函数返回所需的最小分钟数;如果没有找到路径,则返回-1
,表示被困在迷宫中。主函数读取迷宫数据,并对每组测试数据调用bfs
函数,最后输出从起点到终点的最小移动次数。
dfs模板
#include <iostream>
#include <vector>
using namespace std;
int N, M; // 全局变量,表示土地的行数和列数
// 八个方向:上、下、左、右以及四个对角线方向
int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
void dfs(vector<vector<char>>& land, int x, int y) {
land[x][y] = '.'; // 标记当前位置为已访问
// 遍历八个方向
for (int i = 0; i < 8; ++i) {
int nx = x + dx[i], ny = y + dy[i];
// 检查新位置是否在土地内,并且是否为水
if (nx >= 0 && nx < N && ny >= 0 && ny < M && land[nx][ny] == 'W') {
dfs(land, nx, ny); // 递归访问
}
}
}
int main() {
cin >> N >> M;
vector<vector<char>> land(N, vector<char>(M));
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
cin >> land[i][j];
}
}
int puddles = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (land[i][j] == 'W') {
dfs(land, i, j); // 从有水的地方开始DFS
++puddles; // 找到一个水洼
}
}
}
cout << puddles << endl; // 输出水洼总数
return 0;
}
1250:The Castle
要使用广度优先搜索(BFS)解决这个问题,我们可以采用以下思路:
-
初始化:创建一个二维数组来表示城堡的每个方块是否已被访问过,以及一个表示城堡的二维数组来存储每个方块的墙的配置。
-
解析墙的配置:对于城堡中的每个方块,使用位运算来判断方块的四面墙是否存在。墙的配置用一个0到15的数字表示,每个位表示一面墙(例如,二进制的1011表示除东墙外的三面墙都存在)。
-
广度优先搜索(BFS):
- 对于每个未被访问过的方块,使用BFS来遍历所有通过可走的路径(即没有墙阻挡的方向)相连的方块。
- 使用队列来实现BFS。初始时,将当前方块加入队列。
- 在BFS过程中,计算当前连通区域的大小(即房间的大小)。
- 将遍历到的方块标记为已访问。
-
统计结果:
- 维护一个计数器来统计房间的总数。
- 使用一个变量来记录遍历过程中找到的最大房间的大小。
-
输出结果:
- 输出房间总数和最大房间的大小。
下面是使用广度优先搜索算法解决问题的C++代码示例:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAX = 50;
vector<vector<int>> castle(MAX, vector<int>(MAX));
vector<vector<bool>> visited(MAX, vector<bool>(MAX, false));
int m, n;
// 方向:北,东,南,西
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
int wall[4] = {2, 4, 8, 1};
int bfs(int x, int y) {
queue<pair<int, int>> q;
q.push({x, y});
visited[x][y] = true;
int area = 0;
while (!q.empty()) {
int cx = q.front().first;
int cy = q.front().second;
q.pop();
area++;
for (int i = 0; i < 4; ++i) {
int nx = cx + dx[i];
int ny = cy + dy[i];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && !visited[nx][ny] && (castle[cx][cy] & wall[i]) == 0) {
visited[nx][ny] = true;
q.push({nx, ny});
}
}
}
return area;
}
int main() {
cin >> m >> n;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cin >> castle[i][j];
}
}
int rooms = 0, maxArea = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (!visited[i][j]) {
rooms++;
maxArea = max(maxArea, bfs(i, j));
}
}
}
cout << rooms << endl;
cout << maxArea << endl;
return 0;
}
这段代码首先读入城堡的尺寸和每个方块的墙的配置。然后,它遍历城堡中的每个方块,对于每个未访问过的方块,使用BFS来找出所有相连的方块,计算出房间的大小。最后,输出房间的总数和最大房间的大小。
1251:仙岛求药
#include <iostream>
#include <vector>
#include <queue>
#include <cstring> // 如果需要使用memset,对于vector不必要
using namespace std;
const int MAX = 30; // 预定义的最大尺寸
vector<vector<char>> a; // 使用vector<vector<char>>代替原生数组
struct Node {
int x, y, step;
};
int fx[4] = {0, 0, 1, -1};
int fy[4] = {1, -1, 0, 0};
int bfs(int s1, int s2, int e1, int e2) {
queue<Node> q;
q.push({s1, s2, 0});
a[s1][s2] = '#'; // 标记起点为已访问
while (!q.empty()) {
Node current = q.front();
q.pop();
if (current.x == e1 && current.y == e2) {
return current.step; // 找到终点,返回步数
}
for (int i = 0; i < 4; i++) {
int tx = current.x + fx[i];
int ty = current.y + fy[i];
if (tx >= 0 && tx < a.size() && ty >= 0 && ty < a[0].size() && (a[tx][ty] == '.' || a[tx][ty] == '*')) { // 可以走的路径
a[tx][ty] = '#'; // 标记为已访问
q.push({tx, ty, current.step + 1});
}
}
}
return -1; // 无法到达终点
}
int main() {
int n, m;
while (cin >> n >> m && (n || m)) {
a.resize(n, vector<char>(m)); // 使用resize为地图分配空间
int s1, s2, e1, e2; // 起点和终点坐标
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
if (a[i][j] == '@') { // 起点
s1 = i;
s2 = j;
}
if (a[i][j] == '*') { // 终点
e1 = i;
e2 = j;
}
}
}
cout << bfs(s1, s2, e1, e2) << endl;
}
return 0;
}
1252:走迷宫
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Node {
int x, y, step;
};
// 方向数组,表示上、下、左、右四个方向的移动
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
int bfs(const vector<vector<char>>& maze, int R, int C) {
vector<vector<bool>> visited(R, vector<bool>(C, false)); // 访问状态数组
queue<Node> q;
q.push({0, 0, 1}); // 从左上角开始,步数为1(包括起点)
visited[0][0] = true;
while (!q.empty()) {
Node current = q.front();
q.pop();
// 到达右下角
if (current.x == R - 1 && current.y == C - 1) {
return current.step;
}
// 尝试四个方向的移动
for (int i = 0; i < 4; ++i) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
if (nx >= 0 && nx < R && ny >= 0 && ny < C && !visited[nx][ny] && maze[nx][ny] == '.') {
visited[nx][ny] = true;
q.push({nx, ny, current.step + 1});
}
}
}
return -1; // 如果无法到达终点(虽然题目保证可以到达)
}
int main() {
int R, C;
cin >> R >> C;
vector<vector<char>> maze(R, vector<char>(C));
for (int i = 0; i < R; ++i) {
for (int j = 0; j < C; ++j) {
cin >> maze[i][j];
}
}
cout << bfs(maze, R, C) << endl;
return 0;
}
1253:抓住那头牛
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int MAX = 100001; // 数轴的最大范围
int bfs(int N, int K) {
vector<int> visited(MAX, 0); // 访问数组,同时记录到达每个位置的最短时间
queue<int> q;
q.push(N); // 将起始位置入队
visited[N] = 1; // 标记起始位置已访问,初始化为1而不是0,用于避免特殊情况N=0
while (!q.empty()) {
int current = q.front();
q.pop();
// 如果当前位置就是牛的位置,返回当前已经花费的时间
if (current == K) {
return visited[current] - 1; // 返回结果时减去初始的1
}
// 尝试三种移动方式
int nextPos = current - 1;
if (nextPos >= 0 && !visited[nextPos]) {
visited[nextPos] = visited[current] + 1;
q.push(nextPos);
}
nextPos = current + 1;
if (nextPos < MAX && !visited[nextPos]) {
visited[nextPos] = visited[current] + 1;
q.push(nextPos);
}
nextPos = current * 2;
if (nextPos < MAX && !visited[nextPos]) {
visited[nextPos] = visited[current] + 1;
q.push(nextPos);
}
}
return -1; // 如果没有找到路径,返回-1(理论上不会发生,因为题目保证了解的存在)
}
int main() {
int N, K;
cin >> N >> K;
cout << bfs(N, K) << endl;
return 0;
}
1254:走出迷宫
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 100;
vector<vector<char>> maze;
vector<vector<bool>> visited;
int dx[4] = {0, 0, -1, 1}; // 上下左右移动
int dy[4] = {-1, 1, 0, 0};
int n, m; // 迷宫的行数和列数
struct Node {
int x, y, step;
};
int bfs(int sx, int sy, int ex, int ey) {
queue<Node> q;
q.push({sx, sy, 0});
visited[sx][sy] = true;
while (!q.empty()) {
Node current = q.front();
q.pop();
if (current.x == ex && current.y == ey) {
return current.step;
}
for (int i = 0; i < 4; ++i) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && maze[nx][ny] != '#') {
visited[nx][ny] = true;
q.push({nx, ny, current.step + 1});
}
}
}
return -1; // 如果没有路径可以到达
}
int main() {
cin >> n >> m;
maze.resize(n, vector<char>(m)); // 使用resize为迷宫分配空间
visited.resize(n, vector<bool>(m, false)); // 同样为访问状态分配空间
int sx, sy, ex, ey; // 起点和终点坐标
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> maze[i][j];
if (maze[i][j] == 'S') {
sx = i;
sy = j;
}
if (maze[i][j] == 'T') {
ex = i;
ey = j;
}
}
}
cout << bfs(sx, sy, ex, ey) << endl;
return 0;
}
1255:迷宫问题
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Node {
int x, y;
vector<pair<int, int>> path; // 记录到达该节点的路径
};
int maze[5][5]; // 迷宫矩阵
int dx[4] = {0, 0, -1, 1}; // 上下左右移动
int dy[4] = {-1, 1, 0, 0};
void bfs() {
queue<Node> q;
Node start = {0, 0}; // 起始点
start.path.push_back({0, 0}); // 路径初始化
q.push(start);
maze[0][0] = 1; // 标记起始点为已访问
while (!q.empty()) {
Node current = q.front();
q.pop();
if (current.x == 4 && current.y == 4) { // 到达终点
for (const auto p : current.path) { // 打印路径
cout << "(" << p.first << ", " << p.second << ")" << endl;
}
return;
}
for (int i = 0; i < 4; ++i) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
// 直接在循环中检查(nx, ny)是否是有效位置
if (nx >= 0 && nx < 5 && ny >= 0 && ny < 5 && maze[nx][ny] == 0) {
maze[nx][ny] = 1; // 标记为已访问
Node next = {nx, ny, current.path};//把之前的路径给新的点
next.path.push_back({nx, ny}); // 将新的路径接到后面
q.push(next);
}
}
}
}
int main() {
// 输入迷宫矩阵
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 5; ++j) {
cin >> maze[i][j];
}
}
bfs(); // 执行BFS搜索
return 0;
}
1256:献给阿尔吉侬的花束
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXR = 200, MAXC = 200;
int dx[4] = {0, 0, -1, 1}; // 上下左右移动
int dy[4] = {-1, 1, 0, 0};
struct Node {
int x, y, step;
};
int bfs(vector<string>& maze, int R, int C, int sx, int sy, int ex, int ey) {
vector<vector<bool>> visited(R, vector<bool>(C, false));
queue<Node> q;
q.push({sx, sy, 0});
visited[sx][sy] = true;
while (!q.empty()) {
Node current = q.front(); q.pop();
if (current.x == ex && current.y == ey) return current.step;
for (int i = 0; i < 4; ++i) {
int nx = current.x + dx[i], ny = current.y + dy[i];
if (nx >= 0 && nx < R && ny >= 0 && ny < C && !visited[nx][ny] && maze[nx][ny] != '#') {
visited[nx][ny] = true;
q.push({nx, ny, current.step + 1});
}
}
}
return -1; // 如果无法到达E
}
int main() {
int T;
cin >> T;
while (T--) {
int R, C, sx, sy, ex, ey;
cin >> R >> C;
vector<string> maze(R);
for (int i = 0; i < R; ++i) {
cin >> maze[i];
for (int j = 0; j < C; ++j) {
if (maze[i][j] == 'S') sx = i, sy = j;
else if (maze[i][j] == 'E') ex = i, ey = j;
}
}
int result = bfs(maze, R, C, sx, sy, ex, ey);
if (result != -1) cout << result << endl;
else cout << "oop!" << endl;
}
return 0;
}
1257:Knight Moves
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Position {
int x, y, step;
};
// 马的8种可能移动
int dx[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[8] = {-1, 1, -2, 2, -2, 2, -1, 1};
int bfs(int L, int sx, int sy, int ex, int ey) {
vector<vector<bool>> visited(L, vector<bool>(L, false));
queue<Position> q;
q.push({sx, sy, 0});
visited[sx][sy] = true;
while (!q.empty()) {
Position current = q.front();
q.pop();
if (current.x == ex && current.y == ey) {
return current.step;
}
for (int i = 0; i < 8; ++i) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
if (nx >= 0 && nx < L && ny >= 0 && ny < L && !visited[nx][ny]) {
visited[nx][ny] = true;
q.push({nx, ny, current.step + 1});
}
}
}
return -1; // 这种情况理论上不会发生,因为题目保证了解的存在
}
int main() {
int t;
cin >> t;
while (t--) {
int L, sx, sy, ex, ey;
cin >> L >> sx >> sy >> ex >> ey;
cout << bfs(L, sx, sy, ex, ey) << endl;
}
return 0;
}