代码
#include <iostream>
#include <queue>
using namespace std;
const int MAX = 100000;
struct node {
int x;
int y;
int value; //节点类型[0,1,2,3]
int remindTime; //距离爆炸的剩余时间
int minStep; //到此位置时的移动步数
};
node a[10][10]; //因为题目中N,M最大为8
int N, M; //row,column
queue<node> q;
//规范来说,此函数不应该称为BFS,而只是对一个点进行的操作。
//当节点类型为1时,可以移动。为0时,不能移动。为3时,到达终点。为4时,将爆炸事件重置为6,。
//此外,此题的特殊处理是:每个点可以反复走。也就是不能用map将走过的点设为1。但这样会造成死循环
//其实,节点类型为4的点,在走了一次之后,应该变成0。也就是说,将爆炸时间重置的这些点,不能反复走!其他位置是可以反复走的!
//BFS应该是取出队头后,对其周围四个点进行一次遍历。
bool bfs(node top, int x, int y) {
bool flag = false;
if(x>=1 && x<=N && y>=1 && y<=M) {
node newnode = a[x][y];
if(newnode.value==1 && top.remindTime>1) {
newnode.remindTime = top.remindTime - 1;
newnode.minStep = top.minStep + 1;
q.push(newnode);
}
if(newnode.value==3 && top.remindTime>1) {
flag = true;
}
if(newnode.value==4 && top.remindTime>1) {
newnode.remindTime = 6;
newnode.minStep = top.minStep + 1;
a[x][y].value = 0;
q.push(newnode);
}
}
return flag;
}
int main() {
int T;
scanf("%d", &T);
for(int i=0; i<T; i++) {
scanf("%d %d", &N, &M);
int startX, startY;
for(int j=1; j<=N; j++) {
for(int k=1; k<=M; k++) {
a[j][k].x = j;
a[j][k].y = k;
a[j][k].minStep = MAX;
scanf("%d", &a[j][k].value);
if(a[j][k].value==2) { //节点类型为2,是起始位置。
startX = j;
startY = k;
a[j][k].remindTime = 6;
a[j][k].minStep = 0;
}
}
}
int ans = -1; //初始化为-1,当没有答案时也能正常输出。
q = queue<node>(); //clear the queue
q.push(a[startX][startY]);
while(!q.empty()) {
node top = q.front();
q.pop();
//对队头元素邻居遍历。
bool up = bfs(top, top.x-1, top.y);
bool left = bfs(top, top.x, top.y-1);
bool down = bfs(top, top.x+1, top.y);
bool right = bfs(top, top.x, top.y+1);
if(up || left || down || right) { //找到了出口,就break
ans = top.minStep + 1;
break;
}
}
printf("%d\n", ans);
}
return 0;
}
注解
本题与普通的BFS的区别是:每个点可以反复走。
节点类型为4的点,在走了一次之后,应该变成0。也就是说,将爆炸时间重置的这些点,不能反复走!其他位置是可以反复走的!