P2895 [USACO08FEB] Meteor Shower S
题目理解
这道题目描述了一个牧场上的流星雨场景,贝茜需要从原点(0,0)出发,寻找一个永远不会被流星击中的安全位置。每个流星会在特定时间击中某个坐标,并摧毁该位置及其上下左右相邻的四个格子。贝茜每单位时间可以移动一格,但不能进入已被摧毁或即将被摧毁的格子。
解题思路
-
预处理流星影响:首先记录每个格子最早被流星摧毁的时间。如果一个格子不会被任何流星摧毁,则标记为-1。
-
广度优先搜索(BFS):从起点(0,0)开始进行BFS,记录到达每个格子的时间。在移动时,必须确保到达该格子的时间早于该格子被摧毁的时间。
-
安全位置判断:在BFS过程中,如果遇到一个永远不会被摧毁的格子(G[xx][yy] == -1),则立即返回当前时间作为答案。如果队列为空仍未找到安全位置,则返回-1。
参考代码
#include<bits/stdc++.h>
#define N 305 // 定义坐标的最大范围
#define endl "\n"
using namespace std;
// 定义坐标和时间结构体
struct node {
int x, y, t;
};
queue<node> q; // BFS使用的队列
int G[N][N]; // 记录每个格子被摧毁的最早时间,-1表示安全
int vis[N][N]; // 标记数组,记录是否访问过
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; // 四个方向的移动增量
int n, m; // n未使用,m是流星数量
bool f; // 标记是否找到安全位置
// 广度优先搜索函数,从(sx,sy)出发寻找安全位置
void bfs(int sx, int sy) {
node s = {sx, sy, 0}; // 起点,时间为0
vis[s.x][s.y] = 1; // 标记为已访问
q.push(s); // 入队
while (!q.empty()) {
s = q.front();
// 检查四个方向
for (int i = 0; i < 4; i++) {
int xx = s.x + dx[i], yy = s.y + dy[i], tt = s.t + 1;
// 如果找到安全位置
if (G[xx][yy] == -1 && xx >= 0 && yy >= 0) {
cout << tt << endl; // 输出到达时间
f = 1; // 标记已找到
return;
}
// 如果新位置在有效范围内、未被访问过,且到达时间早于被摧毁时间
if (xx >= 0 && yy >= 0 && vis[xx][yy] == 0 && tt < G[xx][yy]) {
vis[xx][yy] = 1; // 标记为已访问
q.push({xx, yy, tt}); // 入队继续搜索
}
}
q.pop();
}
}
int main() {
cin >> m; // 输入流星数量
memset(G, -1, sizeof(G)); // 初始化所有格子为安全状态
// 处理每个流星的影响
while (m--) {
int x, y, t;
cin >> x >> y >> t;
// 更新流星撞击点的时间(取最早时间)
if (x >= 0 && y >= 0 && (t < G[x][y] || G[x][y] == -1)) {
G[x][y] = t;
}
// 更新四个相邻格子的时间
for (int i = 0; i < 4; i++) {
int xx = x + dx[i], yy = y + dy[i];
if (xx >= 0 && yy >= 0 && (t < G[xx][yy] || G[xx][yy] == -1)) {
G[xx][yy] = t;
}
}
}
// 检查起点状态
if (G[0][0] == 0) { // 起点一开始就被摧毁
cout << -1;
return 0;
}
if (G[0][0] == -1) { // 起点本身就是安全位置
cout << 0;
return 0;
}
bfs(0, 0); // 从起点开始BFS
if (!f) cout << -1; // 未找到安全位置
return 0;
}
总结
这道题的关键在于:
-
预处理每个格子的最早被摧毁时间
-
使用BFS进行最短路径搜索时,需要考虑时间限制条件
-
及时终止条件(找到第一个安全位置)

浙公网安备 33010602011771号