洛谷P2895Meteor Shower S

文章讲述了如何利用BreadthFirstSearch(BFS)算法解决一个IT技术问题,计算贝茜在流星雨影响下到达安全位置的最短时间,避开流星坠落区域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。

如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。

根据预报,一共有 M 颗流星(1≤M≤50,000) 会坠落在农场上,其中第 i 颗流星会在时刻(0≤Ti​≤1000)砸在坐标为 (Xi​,Yi​)(0≤Xi​,Yi​≤300)的格子里。流星的力量会将它所在的格子,以及周围 44 个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。

贝茜在时刻 00 开始行动,她只能在第一象限中,平行于坐标轴行动,每 11 个时刻中,她能移动到相邻的(一般是 44 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 t 被流星撞击或烧焦,那么贝茜只能在 t 之前的时刻在这个格子里出现。 贝茜一开始在 (0,0)。

请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 −1−1。

输入格式

共 M+1 行,第 1 行输入一个整数 M,接下来的 M 行每行输入三个整数分别为Xi​,Yi​,Ti​。

输出格式

贝茜到达安全地点所需的最短时间,如果不可能,则为 −1。

这道题刚拿到手我的思路并不是bfs而是暴力,方块进行遍历,如果安全,用行数列数相减就可以计算出步数,但是好像不太对,直接放弃。老老实实用bfs写。

最开始思路就是用一个二维数组star,将流星落下的位置以及周围四个位置赋值为落下的时间,但是还是要分析先后顺序,如果重复赋值要取最小的那个时间值。然后就是从(0,0)开始遍历,本来是想着省变量直接在star数组上面搜索,写了半天总有小错误,直接也不省变量了,又开一个二维数组g用来记录步数。不过需要注意的是,将star数组初始化为1e9,也就是无穷大,代表方块永远没有流星落下。接下来就是模板了,直接上代码

变量解释

star:记录流行落下时间及坐标

g:记录步数

go:偏移量数组

cont队列:这就不必解释了吧应该

//Meteor Shower S
#include "iostream"
#include "queue"
#include "cstring"
using namespace std;
int n;
int star[305][305];
int g[305][305];
int go[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
typedef pair<int,int>pos;
queue<pos>cont;
int bfs(){
    cont.push({0,0});//起点进队
    while(!cont.empty()){//标准模板
        pos temp=cont.front();
        int x=temp.first,y=temp.second;
        cont.pop();
        for(int i=0;i<4;i++){//找四个方向
            int nx=x+go[i][0],ny=y+go[i][1];
            if(nx<0||ny<0)continue;//超限,过掉
            if(g[nx][ny])continue;//走过,剪枝过掉,一个格子永远不重复走第二次
            if(g[x][y]+1>=star[nx][ny])continue;//步数大于等于流星落下的那么会被砸死,过掉
            g[nx][ny]=g[x][y]+1;//所有不被过掉的坐标都可以走,赋值为+1
            cont.push({nx,ny});//入队,为了下次遍历
            if(star[nx][ny]>1e9)return g[nx][ny];//如果这个方格可以走并且不会被砸,直接返回走到此格所需步数
        }
    }
    return -1;//中间一直没有return就代表无法走出返回-1
}
int main(){
    cin>>n;
    memset(star,0x3f,sizeof star);//初始化为无穷大
    for(int i=0;i<n;i++){
        int x,y,flag;
        cin>>x>>y>>flag;//输入条件,flag为落下时间
        star[x][y]=min(star[x][y],flag);//取最小时间
        for(int j=0;j<4;j++){//四个方向赋值
            int nx=x+go[j][0],ny=y+go[j][1];
            if(nx<0||ny<0)continue;//超限,过掉
            star[nx][ny]=min(star[nx][ny],flag);//依然是取最早的时间,也就是最小值
        }
    }
    int ans=bfs();
    cout<<ans;//输出结果
}

### 解决方案 USACO 的题目 **P2895 Meteor Shower S** 是一道经典的 BFS(广度优先搜索)问题,涉及路径规划以及动态障碍物的处理。以下是关于此题目的 C++ 实现方法及相关讨论。 #### 1. 题目概述 贝茜需要在一个二维网格上移动到尽可能远的位置,同时避开由流星造成的破坏区域。每颗流星会在特定时间落在某个位置,摧毁其周围的五个单元格(中心及其上下左右)。目标是最小化贝茜受到的风险计算最短到达安全地点的时间[^5]。 --- #### 2. 关键算法思路 为了高效解决这个问题,可以采用以下策略: - 使用 **BFS(广度优先搜索)** 来模拟贝茜可能的行走路线。 - 动态更新地图上的危险区域,确保在每个时刻只考虑有效的威胁。 - 提前预处理所有流星的影响范围,减少冗余计算。 由于直接在每次 BFS 中调用 `boom` 函数可能导致性能瓶颈[^4],因此可以通过优化来降低复杂度。 --- #### 3. 优化建议 为了避免重复标记已知的危险区域,可以在程序初始化阶段完成如下操作: - 创建一个数组记录每个单位时间内哪些坐标会被流星影响。 - 将 BFS 和流星爆炸事件同步进行,仅在必要时扩展新的状态。 这种方法能够显著提升运行速度,尤其对于大规模输入数据(如 $ M \leq 50,000 $),效果尤为明显。 --- #### 4. C++ 示例代码实现 下面提供了一个高效的解决方案框架: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e6; int grid[1001][1001]; // 地图大小假设为合理范围内 bool visited[1001][1001]; queue<pair<int, pair<int, int>>> q; // 存储 {time, {x, y}} // 方向向量定义 vector<pair<int, int>> directions = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} }; void initializeGrid(int N, vector<tuple<int, int, int>>& meteors) { memset(grid, 0, sizeof(grid)); for(auto &[t, x, y] : meteors){ if(t >= N || t < 0) continue; // 超过最大时间或负数忽略 grid[x][y] = max(grid[x][y], t); for(auto &[dx, dy] : directions){ int nx = x + dx, ny = y + dy; if(nx >=0 && nx <1001 && ny>=0 && ny<1001){ grid[nx][ny] = max(grid[nx][ny], t); } } } } bool isValid(int time, int x, int y){ return !(grid[x][y] <= time); // 如果当前时间<=流星爆炸时间则不可通过 } int main(){ ios::sync_with_stdio(false); cin.tie(0); int T, X, Y; cin >> T >> X >> Y; vector<tuple<int, int, int>> meteors(T); for(int i=0;i<T;i++) cin >> get<0>(meteors[i]) >> get<1>(meteors[i]) >> get<2>(meteors[i]); initializeGrid(X*Y, meteors); memset(visited, false, sizeof(visited)); q.push({0,{X,Y}}); visited[X][Y]=true; while(!q.empty()){ auto current = q.front(); q.pop(); int currentTime = current.first; int cx = current.second.first, cy = current.second.second; if(isValid(currentTime,cx,cy)){ cout << currentTime; return 0; } for(auto &[dx,dy]:directions){ int nx=cx+dx,ny=cy+dy; if(nx>=0&&nx<1001&&ny>=0&&ny<1001&&!visited[nx][ny]){ if(isValid(currentTime,nx,ny)){ q.push({currentTime+1,{nx,ny}}); visited[nx][ny]=true; } } } } cout << "-1"; // 若无解返回-1 return 0; } ``` 上述代码实现了基于 BFS 的最优路径查找逻辑,预先构建了流星影响的地图以加速查询过程。 --- #### 5. 进一步讨论 尽管本题的核心在于 BFS 及动态更新机制的应用,但在实际编码过程中仍需注意以下几个方面: - 输入规模较大时应选用快速 IO 方法(如关闭同步流 `ios::sync_with_stdio(false)` 取消绑定 `cin.tie(NULL)`)。 - 对于超出边界或者无关紧要的数据点可以直接跳过处理,从而节省不必要的运算开销。 - 利用位掩码或其他压缩技术存储访问标志可进一步节约内存资源。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值