《炸弹人》是HUDSON出品的一款ACT类型游戏,经典的第一作登陆在FC版本,游戏于1983年发行。游戏具体操作是一个机器人放置炸弹来炸死敌人,但也可以炸死自己,还有些增强威力与技能道具增加了游戏的可玩性。
接下来我们就对《炸弹人》进行一次简化模拟:
首先我们需要一个 N×M 大小的矩阵来存放地图,矩阵中 '*' 代表没有点燃的炸弹,‘0’代表空地,玩家可以自定义炸弹的威力为 p (由于我们是简化模拟,所以 p 只会取 1 或者 2 );像《炸弹人》一样,炸弹可以引发自己上、下、左、右(斜角不算)各 p 格范围内产生爆炸,并且爆炸可以产生连锁反应,问至少需要进行几次引爆才可以让所有的炸弹都爆炸。
例如:
在 5×7 的矩阵
中,当玩家把炸弹的威力 p 设为 1 时至少需要进行 4 次引爆,
而当玩家把炸弹的威力 p 设为 2 时至少需要进行 3 次引爆。
输入格式:
在第一行定义矩阵的大小 n,m,1≤n,m≤103 和炸弹的威力 p,p∈{1,2},数字之间用空格隔开
随后 n 行是由‘0’和‘*’两种字符组成的 n×m 矩阵,字符之间用空格隔开。
输出格式:
在一行中输出至少需要进行几次引爆可以让所有的炸弹都爆炸。
输入样例1:
5 7 1
0 0 * * 0 0 0
0 0 * 0 0 0 0
0 0 0 0 0 * 0
0 * * 0 0 0 0
0 0 0 * 0 0 0
输出样例1:
4
输入样例2:
5 7 2
0 0 * * 0 0 0
0 0 * 0 0 0 0
0 0 0 0 0 * 0
0 * * 0 0 0 0
0 0 0 * 0 0 0
输出样例2:
3
模拟炸弹人游戏:最少引爆次数计算
问题描述
我们需要模拟经典游戏《炸弹人》中的炸弹引爆过程。给定一个N×M大小的矩阵,其中包含未引爆的炸弹('*')和空地('0')。炸弹的威力p可以是1或2,表示炸弹可以引爆上下左右p格范围内的其他炸弹。爆炸可以产生连锁反应,要求计算最少需要主动引爆多少次才能让所有炸弹爆炸。
解题思路
关键点分析
- 炸弹威力影响范围:p=1时影响相邻4格,p=2时影响相邻8格(上下左右各2格)
- 连锁反应:一个炸弹被引爆后会自动引爆其威力范围内的其他炸弹
- 最少引爆次数:需要找到最少的引爆点,使得通过这些点的连锁反应能引爆所有炸弹
算法选择
- 广度优先搜索(BFS):适合模拟爆炸的连锁反应过程
- 连通区域划分:将炸弹矩阵划分为若干个连通区域,每个区域只需引爆一次
代码实现与分析
#include<bits/stdc++.h>
using namespace std;
char grid[10005][10005]; // 存储地图
int visited[10005][10005]; // 访问标记数组
int n, m, p; // 地图大小和炸弹威力
// p=1时的方向数组(上下左右各1格)
int dir1[4][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}};
// p=2时的方向数组(上下左右各2格)
int dir2[8][2] = {{1,0}, {-1,0}, {0,1}, {0,-1},
{2,0}, {-2,0}, {0,2}, {0,-2}};
// p=1时的BFS引爆过程
void bfs1(int x, int y) {
queue<pair<int, int>> q;
q.push({x, y});
grid[x][y] = '0'; // 标记为已引爆
visited[x][y] = 1; // 标记为已访问
while (!q.empty()) {
auto current = q.front();
q.pop();
int x0 = current.first;
int y0 = current.second;
// 检查四个方向
for (int i = 0; i < 4; i++) {
int x1 = x0 + dir1[i][0];
int y1 = y0 + dir1[i][1];
// 检查边界和是否未访问
if (x1 > 0 && x1 <= n && y1 > 0 && y1 <= m) {
if (grid[x1][y1] == '*' && !visited[x1][y1]) {
grid[x1][y1] = '0'; // 引爆该炸弹
visited[x1][y1] = 1; // 标记为已访问
q.push({x1, y1}); // 加入队列继续传播
}
}
}
}
}
// p=2时的BFS引爆过程
void bfs2(int x, int y) {
queue<pair<int, int>> q;
q.push({x, y});
grid[x][y] = '0'; // 标记为已引爆
visited[x][y] = 1; // 标记为已访问
while (!q.empty()) {
auto current = q.front();
q.pop();
int x0 = current.first;
int y0 = current.second;
// 检查八个方向
for (int i = 0; i < 8; i++) {
int x1 = x0 + dir2[i][0];
int y1 = y0 + dir2[i][1];
// 检查边界和是否未访问
if (x1 > 0 && x1 <= n && y1 > 0 && y1 <= m) {
if (grid[x1][y1] == '*' && !visited[x1][y1]) {
grid[x1][y1] = '0'; // 引爆该炸弹
visited[x1][y1] = 1; // 标记为已访问
q.push({x1, y1}); // 加入队列继续传播
}
}
}
}
}
int main() {
cin >> n >> m >> p;
int count = 0; // 记录最少引爆次数
// 读取地图数据
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> grid[i][j];
visited[i][j] = 0; // 初始化访问标记
}
}
// 根据威力选择不同的引爆策略
if (p == 1) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (grid[i][j] == '*') {
bfs1(i, j); // 引爆并传播
count++; // 增加引爆计数
}
}
}
}
else if (p == 2) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (grid[i][j] == '*') {
bfs2(i, j); // 引爆并传播
count++; // 增加引爆计数
}
}
}
}
cout << count << endl;
return 0;
}
代码解析
-
数据结构:
grid
数组存储地图信息visited
数组记录炸弹是否已被引爆- 方向数组
dir1
和dir2
分别对应p=1和p=2时的爆炸范围
-
BFS函数:
bfs1()
处理p=1的情况,引爆上下左右相邻1格的炸弹bfs2()
处理p=2的情况,引爆上下左右相邻2格的炸弹- 两种BFS都会通过队列实现连锁反应的模拟
-
主函数:
- 读取输入数据并初始化
- 根据p值选择不同的引爆策略
- 遍历地图,对每个未引爆的炸弹执行BFS并计数
复杂度分析
- 时间复杂度:O(n×m),需要遍历整个矩阵
- 空间复杂度:O(n×m),用于存储地图和访问标记