1 题意
给定一个地图,如下图所示:
2 1 1 0 1 1 1 0
1 0 4 1 1 0 4 1
1 0 0 0 0 0 0 1
1 1 1 4 1 1 1 3
地图上的标识解释如下:
符号 | 含义 |
---|---|
0 | 代表墙,不可经过 |
1 | 可经过 |
2 | 起点 |
3 | 终点 |
4 | 炸弹重置装置 |
初始时,炸弹的爆炸时间为
6
6
6分钟。向相邻的格子移动需要
1
1
1分钟,并且每次遇到炸弹重置装置时,时间会重置为
6
6
6分钟(如果走到炸弹重置装置的位置是时间已经为
0
0
0分钟,则不能重置)。问:是否能在炸弹爆炸之前走到终点(刚好
0
0
0分钟走到终点不算),如果能走到终点则输出最短时间,否则输出
−
1
-1
−1。
链接:link。
2 思路
2.1 BFS
从起点开始 B F S BFS BFS,每次遍历到一个点的时候都要比较一下此次爆照时间和之前的爆炸时间,如果此次爆炸时间更长,则重新将该点加入队列。
2.1.1 时间复杂度分析
某个点被重新加入队列,可能是由于某个炸弹重置装置导致的,由于最多只有 O ( n m ) \mathcal{O}(nm) O(nm)个炸弹重置装置,所以时间复杂度为 O ( n 2 m 2 ) \mathcal{O}(n^{2}m^{2}) O(n2m2)。
2.1.2 实现
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int n,m,maze[8][8],pre[8][8];
int dir[4][2]={0,-1,0,1,-1,0,1,0};
struct Node{
int x,y,step,time;
Node(int x=0,int y=0,int step=0,int time=0):x(x),y(y),step(step),time(time){}
};
int bfs(int x,int y){
memset(pre,0,sizeof(pre));
queue<Node> que;
que.push(Node(x,y,0,6));
while(!que.empty()){
Node cur=que.front();que.pop();
if(cur.time==0) continue;
if(maze[cur.x][cur.y]==3) return cur.step;
else if(maze[cur.x][cur.y]==4) cur.time=6;
for(int i=0;i<4;i++){
Node nxt(cur.x+dir[i][0],cur.y+dir[i][1]);
if(nxt.x>=0&&nxt.x<n&&nxt.y>=0&&nxt.y<m&&maze[nxt.x][nxt.y]){
nxt.step=cur.step+1;
nxt.time=cur.time-1;
if(pre[nxt.x][nxt.y]<nxt.time){
que.push(nxt);
pre[nxt.x][nxt.y]=nxt.time;
}
}
}
}
return -1;
}
int main(){
int T;cin>>T;
while(T--){
cin>>n>>m;
int x,y;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>maze[i][j];
if(maze[i][j]==2){x=i;y=j;}
}
}
cout<<bfs(x,y)<<endl;
}
return 0;
}