文章目录

输入样例
cpp
4
0 0 2
2 1 2
1 1 2
0 3 5
输出样例
cpp
5
本题的核心是在存在外界干扰(流星坠落)的情况下,使用广度优先搜索(BFS)算法找出贝茜到达安全格子所需的最短时间。
算法思路
- 输入处理与初始化
- 读取流星信息 :从输入中读取流星的数量
m
,以及每颗流星坠落的坐标(x, y)
和坠落时间t
。 - 初始化危险时间矩阵 :创建一个二维数组
destroy
,大小为N x N
(N
为地图的最大可能边长),将数组中每个元素初始化为INT_MAX
,表示每个格子初始时都被认为是永远安全的。 - 初始化距离矩阵 :创建一个二维数组
dist
,同样大小为N x N
,将数组中每个元素初始化为 -1,表示每个格子初始时都未被访问过。
- 预处理危险时间矩阵
- 遍历流星信息 :对于每颗流星,根据其坠落的坐标
(x, y)
和坠落时间t
,更新该格子以及其周围 4 个相邻格子(上、下、左、右)的destroy
。 - 取最小值更新 :在更新
destroy
时,使用min
函数确保记录的是该格子首次变得危险的时间。例如,若某个格子有多颗流星先后坠落,只记录最早使其变得危险的时间。
- 初始化 BFS
- 创建队列 :使用一个队列来进行 BFS 搜索,队列中存储的元素为坐标对
(x, y)
,表示贝茜当前所在的位置。 - 起点入队 :将贝茜的初始位置
(0, 0)
加入队列,并将该位置的dist
设为 0,表示从起点到起点的距离为 0。
- BFS 搜索过程
- 取出队首元素 :从队列中取出队首元素
(x, y)
,同时记录到达该位置的当前时间currentTime
,即dist[x][y]
。 - 检查是否到达安全格子 :检查当前位置的
destroy
是否为INT_MAX
,若是,则说明该位置永远安全,输出当前时间currentTime
并结束搜索。 - 尝试向四个方向移动:
- 计算新位置和新时间 :对于当前位置
(x, y)
,尝试向四个方向(上、下、左、右)移动,计算新位置的坐标(newX, newY)
和到达新位置所需的新时间newTime
(newTime = currentTime + 1
)。 - 边界检查 :检查新位置是否越界(即
newX < 0
或newY < 0
),若越界则跳过该位置。 - 危险检查 :检查新时间
newTime
是否大于等于新位置的destroy
,若大于等于则说明新位置在贝茜到达时已经变得危险,跳过该位置。 - 未访问检查 :检查新位置的
dist
是否为 -1,若为 -1 则说明该位置未被访问过,更新dist[newX][newY]
为newTime
,并将新位置(newX, newY)
加入队列。
- 计算新位置和新时间 :对于当前位置
- 结果判断
- 队列为空:若队列遍历完仍未找到安全的格子,说明贝茜无法到达安全地点,输出 -1。
代码示例
cpp
#include<iostream>
#include<vector>
#include<queue>
#include<climits>
using namespace std;
typedef pair<int,int> PII;
const int N=310;
int destroy[N][N];//记录每个格子被摧毁的最早时间
int dist[N][N];//记录到达每个格子的最短时间
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};
int bfs(){
queue<PII> q;
q.push({0,0});
dist[0][0]=0;
while(!q.empty()){
auto t=q.front();//将起点入队
q.pop();
int x=t.first,y=t.second;
//如果当前位置安全且永远不会被流星摧毁,返回到达该位置的时间
if(destroy[x][y] == INT_MAX) return dist[x][y];
//尝试向四个方向移动
for(int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
int newTime=dist[x][y]+1;
//检查新位置是否越界
if(nx<0 || ny<0) continue;
//检查新位置是否已经变得危险
if(newTime>=destroy[nx][ny]) continue;
//如果新位置未被访问,更新距离并加入队列
if(dist[nx][ny] == -1){
dist[nx][ny] = newTime;
q.push({nx,ny});
}
}
}
return -1;
}
int main(){
int m;
cin>>m;
//初始化到危险时间矩阵为无穷大
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
destroy[i][j] = INT_MAX;
}
}
//初始化距离矩阵为-1,表示未访问过
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
dist[i][j] = -1;
}
}
//读取流星信息并更新危险时间矩阵
for(int i=0;i<m;i++){
int x,y,t;
cin>>x>>y>>t;
//更新当前格子和周围四个格子的被摧毁的最早时间
destroy[x][y] = min(destroy[x][y],t);
for(int j=0;j<4;j++){
int nx=x+dx[j],ny=y+dy[j];
if(nx>=0 && ny>=0){//四周的坐标没有越界
destroy[nx][ny] = min(destroy[nx][ny],t);
}
}
}
int result=bfs();
cout<<result<<endl;
return 0;
}
☆对干扰因素的处理
预处理阶段
预处理阶段的核心目标是提前处理好干扰因素的相关信息,将其转化为便于在 BFS 过程中使用的数据结构,从而减少 BFS 过程中的重复计算,提高算法效率。
数据收集与存储
- 收集干扰信息 :从题目输入中收集所有与干扰因素相关的数据。例如在流星坠落问题中,需要收集每颗流星的坠落坐标
(x, y)
和坠落时间t
。 - 存储干扰信息 :根据干扰因素的特点,选择合适的数据结构来存储这些信息。在流星问题中,使用二维数组
destroyed
来记录每个格子首次变得危险(被摧毁)的时间,初始时将数组元素都设为一个极大值(如INT_MAX
或INF
),表示初始状态下所有格子都是安全的。
干扰信息整合
- 更新干扰状态 :遍历收集到的干扰信息,根据干扰因素的影响范围和规则,更新相应位置的干扰状态。在流星问题中,对于每颗流星,不仅要更新其坠落格子的危险时间,还要更新其周围 4 个相邻格子的危险时间,使用
min
函数确保记录的是最早的危险时间。
每个状态的干扰情况更新
在 BFS 搜索过程中,每个状态(节点)都可能受到干扰因素的影响,因此需要在每次状态转移时更新和检查干扰情况。
状态表示
- 包含干扰信息 :在 BFS 的状态表示中,除了常规的位置信息(如坐标
(x, y)
),还需要包含与干扰因素相关的信息,如到达该位置的时间t
。在流星问题中,队列中存储的元素为(x, y, t)
,表示贝茜在时间t
到达位置(x, y)
。
干扰情况检查
- 判断状态合法性 :在每次状态转移时,根据预处理得到的干扰信息,检查新状态是否合法。在流星问题中,对于新位置
(newX, newY)
,需要检查到达该位置的新时间newTime
是否小于该位置的dangerTime
,如果newTime >= dangerTime[newX][newY]
,则说明该位置在贝茜到达时已经变得危险,该状态不合法,应跳过。
BFS 中的扩展阶段
在 BFS 的扩展阶段,需要根据干扰因素的影响,对状态的扩展规则进行调整,确保只扩展合法的状态。
扩展规则调整
- 考虑干扰因素 :在尝试向相邻状态扩展时,要将干扰因素纳入考虑范围。在流星问题中,从当前位置
(x, y)
向四个方向(上、下、左、右)扩展时,不仅要检查新位置是否越界,还要检查新位置在新时间下是否安全。 - 剪枝优化:利用干扰信息进行剪枝,避免不必要的状态扩展。如果某个状态已经被证明无法到达目标状态(如到达某个位置的时间已经超过该位置的危险时间),则无需对其进行进一步扩展。
记录最优信息
- 更新最优状态 :在扩展过程中,记录到达每个合法状态的最优信息(如最短时间)。在流星问题中,使用
dist
数组记录到达每个格子的最短时间,每次到达一个新的合法位置时,更新该位置的dist
值。
总结
在有外界干扰的 BFS 题型中,通过预处理将干扰因素转化为可查询的数据结构,在状态转移时更新和检查干扰情况,以及在扩展阶段调整扩展规则和进行剪枝优化,可以有效地处理干扰因素,提高算法的效率和正确性。不同的干扰因素可能需要不同的处理方式,但总体思路都是围绕着提前处理、动态检查和合理扩展这几个方面展开。